forked from External/ergo
switch to redis pubsub for ipc
adjust commands to utilize channel names add new config variables fix mention race condition
This commit is contained in:
parent
64ebb1f480
commit
f4c03b6765
12 changed files with 220 additions and 195 deletions
|
|
@ -1067,3 +1067,9 @@ history:
|
||||||
# whether to allow customization of the config at runtime using environment variables,
|
# whether to allow customization of the config at runtime using environment variables,
|
||||||
# e.g., ERGO__SERVER__MAX_SENDQ=128k. see the manual for more details.
|
# e.g., ERGO__SERVER__MAX_SENDQ=128k. see the manual for more details.
|
||||||
allow-environment-overrides: true
|
allow-environment-overrides: true
|
||||||
|
|
||||||
|
cef:
|
||||||
|
imagor:
|
||||||
|
url: "https://example.com/embed/"
|
||||||
|
secret: "secretgoeshere"
|
||||||
|
redis: "redis://user:password@localhost:6379/0?protocol=3"
|
||||||
3
go.mod
3
go.mod
|
|
@ -28,9 +28,12 @@ require (
|
||||||
require (
|
require (
|
||||||
github.com/cshum/imagor v1.4.13
|
github.com/cshum/imagor v1.4.13
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.1
|
github.com/golang-jwt/jwt/v5 v5.2.1
|
||||||
|
github.com/redis/go-redis/v9 v9.6.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
github.com/tidwall/btree v1.4.2 // indirect
|
github.com/tidwall/btree v1.4.2 // indirect
|
||||||
github.com/tidwall/gjson v1.14.3 // indirect
|
github.com/tidwall/gjson v1.14.3 // indirect
|
||||||
github.com/tidwall/grect v0.1.4 // indirect
|
github.com/tidwall/grect v0.1.4 // indirect
|
||||||
|
|
|
||||||
10
go.sum
10
go.sum
|
|
@ -2,10 +2,18 @@ code.cloudfoundry.org/bytefmt v0.0.0-20200131002437-cf55d5288a48 h1:/EMHruHCFXR9
|
||||||
code.cloudfoundry.org/bytefmt v0.0.0-20200131002437-cf55d5288a48/go.mod h1:wN/zk7mhREp/oviagqUXY3EwuHhWyOvAdsn5Y4CzOrc=
|
code.cloudfoundry.org/bytefmt v0.0.0-20200131002437-cf55d5288a48/go.mod h1:wN/zk7mhREp/oviagqUXY3EwuHhWyOvAdsn5Y4CzOrc=
|
||||||
github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962 h1:KeNholpO2xKjgaaSyd+DyQRrsQjhbSeS7qe4nEw8aQw=
|
github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962 h1:KeNholpO2xKjgaaSyd+DyQRrsQjhbSeS7qe4nEw8aQw=
|
||||||
github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962/go.mod h1:kC29dT1vFpj7py2OvG1khBdQpo3kInWP+6QipLbdngo=
|
github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962/go.mod h1:kC29dT1vFpj7py2OvG1khBdQpo3kInWP+6QipLbdngo=
|
||||||
|
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||||
|
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||||
|
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||||
|
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/cshum/imagor v1.4.13 h1:BFcSpsTUOJj+Wv5SzDeXa8bhsT/Ehw7EcrFD0UTdpmU=
|
github.com/cshum/imagor v1.4.13 h1:BFcSpsTUOJj+Wv5SzDeXa8bhsT/Ehw7EcrFD0UTdpmU=
|
||||||
github.com/cshum/imagor v1.4.13/go.mod h1:LHxXgks6Y06GzEHitnlO8vcD5gznxIHWPdvGsnlGpMo=
|
github.com/cshum/imagor v1.4.13/go.mod h1:LHxXgks6Y06GzEHitnlO8vcD5gznxIHWPdvGsnlGpMo=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||||
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ=
|
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ=
|
||||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||||
github.com/ergochat/confusables v0.0.0-20201108231250-4ab98ab61fb1 h1:WLHTOodthVyv5NvYLIvWl112kSFv5IInKKrRN2qpons=
|
github.com/ergochat/confusables v0.0.0-20201108231250-4ab98ab61fb1 h1:WLHTOodthVyv5NvYLIvWl112kSFv5IInKKrRN2qpons=
|
||||||
|
|
@ -40,6 +48,8 @@ github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg=
|
||||||
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
|
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4=
|
||||||
|
github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA=
|
||||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/tidwall/assert v0.1.0 h1:aWcKyRBUAdLoVebxo95N7+YZVTFF/ASTr7BN4sLP6XI=
|
github.com/tidwall/assert v0.1.0 h1:aWcKyRBUAdLoVebxo95N7+YZVTFF/ASTr7BN4sLP6XI=
|
||||||
|
|
|
||||||
241
irc/cef.go
241
irc/cef.go
|
|
@ -1,134 +1,191 @@
|
||||||
package irc
|
package irc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"context"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/cshum/imagor/imagorpath"
|
||||||
"github.com/ergochat/ergo/irc/caps"
|
"github.com/ergochat/ergo/irc/caps"
|
||||||
"io"
|
"github.com/ergochat/irc-go/ircmsg"
|
||||||
"net"
|
"net/http"
|
||||||
"os"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const LISTEN = "0.0.0.0:22843"
|
var ctx = context.Background()
|
||||||
|
|
||||||
type CefConnection struct {
|
func (channel *Channel) RedisBroadcast(message ...string) {
|
||||||
connections map[string]net.Conn
|
if channel.server.redis == nil {
|
||||||
server *Server
|
|
||||||
}
|
|
||||||
|
|
||||||
func (instance *CefConnection) CEFMessage(action string, extra ...string) {
|
|
||||||
if instance == nil {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
str := fmt.Sprintf("%s %s\n", action, strings.Join(extra, " "))
|
err := channel.server.redis.Publish(ctx, "channel."+channel.NameCasefolded(), strings.Join(message, " ")).Err()
|
||||||
for _, conn := range instance.connections {
|
if err != nil {
|
||||||
conn.Write([]byte(str))
|
fmt.Printf("Channel broadcast error: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (instance *CefConnection) KickBroadcast(channel string, user string) {
|
func (client *Client) RedisBroadcast(message ...string) {
|
||||||
instance.CEFMessage("KICK", channel, user)
|
err := client.server.redis.Publish(ctx, "user."+client.NickCasefolded(), strings.Join(message, " ")).Err()
|
||||||
}
|
|
||||||
|
|
||||||
func cefConnection(server *Server) *CefConnection {
|
|
||||||
// create a tcp listener on the given port
|
|
||||||
listener, err := net.Listen("tcp", LISTEN)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Unable to open CefConnection listener:", err)
|
fmt.Printf("User broadcast error: %v", err)
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
fmt.Printf("CefConnection listener on %s active\n", LISTEN)
|
|
||||||
instance := CefConnection{connections: make(map[string]net.Conn), server: server}
|
|
||||||
go cefListener(listener, &instance)
|
|
||||||
|
|
||||||
return &instance
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func cefListener(listener net.Listener, instance *CefConnection) {
|
func (channel *Channel) Broadcast(command string, params ...string) {
|
||||||
// listen for new connections
|
for _, member := range channel.Members() {
|
||||||
for {
|
for _, session := range member.Sessions() {
|
||||||
conn, err := listener.Accept()
|
session.Send(nil, member.server.name, command, params...)
|
||||||
if err != nil {
|
}
|
||||||
fmt.Println("failed to accept cef connection, err:", err)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChannelSub Actions and info centering around channels
|
||||||
|
func (server *Server) ChannelSub() {
|
||||||
|
pubsub := server.redis.PSubscribe(ctx, "channel.*")
|
||||||
|
defer pubsub.Close()
|
||||||
|
|
||||||
|
ch := pubsub.Channel()
|
||||||
|
|
||||||
|
for msg := range ch {
|
||||||
|
server.logger.Info("RedisMessage", msg.Channel, msg.Payload)
|
||||||
|
line := strings.Split(msg.Payload, " ")
|
||||||
|
channelName := strings.SplitN(msg.Channel, ".", 2)[1]
|
||||||
|
channel := server.channels.Get(channelName)
|
||||||
|
if len(line) == 0 {
|
||||||
|
println("Empty string dumped into ", msg.Channel, " channel")
|
||||||
|
}
|
||||||
|
if channel == nil {
|
||||||
|
server.logger.Warning("RedisMessage", "Unknown channel")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
instance.connections[conn.RemoteAddr().String()] = conn
|
|
||||||
|
|
||||||
// pass an accepted connection to a handler goroutine
|
|
||||||
go handleConnection(conn, instance)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleConnection(conn net.Conn, instance *CefConnection) {
|
|
||||||
defer delete(instance.connections, conn.RemoteAddr().String())
|
|
||||||
reader := bufio.NewReader(conn)
|
|
||||||
println("Connection with CEF service established")
|
|
||||||
for {
|
|
||||||
// read client request data
|
|
||||||
bytes, err := reader.ReadBytes(byte('\n'))
|
|
||||||
if err != nil {
|
|
||||||
if err != io.EOF {
|
|
||||||
fmt.Println("failed to read data, err:", err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
convertedLine := string(bytes[:len(bytes)-1])
|
|
||||||
line := strings.Split(strings.Trim(convertedLine, " \n"), " ")
|
|
||||||
fmt.Printf("cef: %+q\n", line)
|
|
||||||
switch line[0] {
|
switch line[0] {
|
||||||
case "PART":
|
case "VOICEPART":
|
||||||
if len(line) == 1 || len(line[1]) == 0 {
|
channel.Broadcast("VOICEPART", channelName, line[1])
|
||||||
println("skipping malformed line")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
channel := instance.server.channels.Get(line[1])
|
|
||||||
for _, member := range channel.Members() {
|
|
||||||
for _, session := range member.Sessions() {
|
|
||||||
session.Send(nil, member.server.name, "VOICEPART", line[1], line[2])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break
|
|
||||||
case "VOICESTATE":
|
case "VOICESTATE":
|
||||||
channel := instance.server.channels.Get(line[1])
|
channel.Broadcast("VOICESTATE", channelName, line[1])
|
||||||
for _, member := range channel.Members() {
|
case "BROADCASTTO":
|
||||||
for _, session := range member.Sessions() {
|
for _, person := range channel.Members() {
|
||||||
session.Send(nil, member.server.name, "VOICESTATE", line[1], line[2], line[3], line[4])
|
person.Send(nil, server.name, line[1], line[2:]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserSub Handles things pertaining to users
|
||||||
|
func (server *Server) UserSub() {
|
||||||
|
pubsub := server.redis.PSubscribe(ctx, "user.*")
|
||||||
|
defer pubsub.Close()
|
||||||
|
|
||||||
|
ch := pubsub.Channel()
|
||||||
|
|
||||||
|
for msg := range ch {
|
||||||
|
server.logger.Info("RedisMessage", msg.Channel, msg.Payload)
|
||||||
|
line := strings.Split(msg.Payload, " ")
|
||||||
|
userName := strings.SplitN(msg.Channel, ".", 2)[1]
|
||||||
|
user := server.clients.Get(userName)
|
||||||
|
if len(line) == 0 {
|
||||||
|
println("Empty string dumped into ", msg.Channel, " channel")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch line[0] {
|
||||||
|
case "FULLYREMOVE":
|
||||||
|
if user != nil {
|
||||||
|
user.destroy(nil)
|
||||||
|
err := server.accounts.Unregister(user.Account(), true)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case "BROADCASTAS":
|
case "BROADCASTAS":
|
||||||
// TODO: global broadcast
|
|
||||||
user := instance.server.clients.Get(line[1])
|
|
||||||
if user != nil {
|
if user != nil {
|
||||||
// I'm not too sure what the capability bit is, I think it's just ones that match
|
// I'm not too sure what the capability bit is, I think it's just ones that match
|
||||||
for friend := range user.Friends(caps.ExtendedNames) {
|
for friend := range user.Friends(caps.ExtendedNames) {
|
||||||
friend.Send(nil, user.NickMaskString(), line[2], line[3:]...)
|
friend.Send(nil, user.NickMaskString(), line[1], line[2:]...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case "BROADCASTTO":
|
|
||||||
channel := instance.server.channels.Get(line[1])
|
|
||||||
if channel != nil {
|
|
||||||
// I'm not too sure what the capability bit is, I think it's just ones that match
|
|
||||||
for _, person := range channel.Members() {
|
|
||||||
person.Send(nil, instance.server.name, line[2], line[3:]...)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
}
|
||||||
case "FULLYREMOVE":
|
|
||||||
user := instance.server.clients.Get(line[1])
|
func startRedis(server *Server) {
|
||||||
if user != nil {
|
go server.ChannelSub()
|
||||||
user.destroy(nil)
|
go server.UserSub()
|
||||||
err := instance.server.accounts.Unregister(user.Account(), true)
|
}
|
||||||
|
|
||||||
|
func (server *Server) GenerateImagorSignaturesFromMessage(message *ircmsg.Message) string {
|
||||||
|
line, err := message.Line()
|
||||||
|
if err == nil {
|
||||||
|
return server.GenerateImagorSignatures(line)
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (server *Server) GetUrlMime(url string) string {
|
||||||
|
config := server.Config()
|
||||||
|
// hacky, should fix
|
||||||
|
if !strings.Contains(url, "?") {
|
||||||
|
url += "?"
|
||||||
|
}
|
||||||
|
params := imagorpath.Params{
|
||||||
|
Image: url,
|
||||||
|
Meta: true,
|
||||||
|
}
|
||||||
|
metaPath := imagorpath.Generate(params, imagorpath.NewHMACSigner(sha256.New, 0, config.Cef.Imagor.Secret))
|
||||||
|
client := http.Client{
|
||||||
|
Timeout: 5 * time.Second,
|
||||||
|
}
|
||||||
|
resp, err := client.Get(config.Cef.Imagor.Url + metaPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
println("Failed on the initial get")
|
||||||
|
println(err.Error())
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
var meta map[string]interface{}
|
||||||
|
err = json.NewDecoder(resp.Body).Decode(&meta)
|
||||||
|
if err != nil {
|
||||||
|
println("Failed on the JSON decode")
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
break
|
contentType, valid := meta["format"].(string)
|
||||||
default:
|
fmt.Printf("%+v\n", meta)
|
||||||
println("Unknown cef message: ", line[0])
|
if !valid {
|
||||||
|
println("No content type")
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return contentType
|
||||||
|
}
|
||||||
|
|
||||||
|
var urlRegex = regexp.MustCompile("https?:\\/\\/[\\w-]+(\\.[\\w-]+)+([\\w.,@?^=%&:/~+#-]*[\\w@?^=%&/~+#-])?")
|
||||||
|
|
||||||
|
// Process a message to add Imagor signatures
|
||||||
|
func (server *Server) GenerateImagorSignatures(str string) string {
|
||||||
|
urls := urlRegex.FindAllString(str, -1)
|
||||||
|
var sigs []string
|
||||||
|
for _, url := range urls {
|
||||||
|
params := imagorpath.Params{
|
||||||
|
Image: url,
|
||||||
|
FitIn: true,
|
||||||
|
Width: 600,
|
||||||
|
Height: 600,
|
||||||
|
}
|
||||||
|
path := imagorpath.Generate(params, imagorpath.NewHMACSigner(sha256.New, 0, server.Config().Cef.Imagor.Secret))
|
||||||
|
signature := path[:strings.IndexByte(path, '/')]
|
||||||
|
contentType := server.GetUrlMime(url)
|
||||||
|
if contentType != "" {
|
||||||
|
sigs = append(sigs, signature+"|"+strings.ReplaceAll(contentType, "/", "_"))
|
||||||
|
} else {
|
||||||
|
sigs = append(sigs, signature)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
if len(sigs) > 0 {
|
||||||
|
return strings.Join(sigs, ",")
|
||||||
|
}
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -802,7 +802,7 @@ func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *Resp
|
||||||
|
|
||||||
client.server.logger.Debug("channels", fmt.Sprintf("%s joined channel %s", details.nick, chname))
|
client.server.logger.Debug("channels", fmt.Sprintf("%s joined channel %s", details.nick, chname))
|
||||||
// I think this is assured to always be a good join point
|
// I think this is assured to always be a good join point
|
||||||
client.server.cefManager.CEFMessage("POLL", channel.NameCasefolded())
|
channel.RedisBroadcast("VOICEPOLL")
|
||||||
|
|
||||||
givenMode := func() (givenMode modes.Mode) {
|
givenMode := func() (givenMode modes.Mode) {
|
||||||
channel.joinPartMutex.Lock()
|
channel.joinPartMutex.Lock()
|
||||||
|
|
@ -999,7 +999,7 @@ func (channel *Channel) playJoinForSession(session *Session) {
|
||||||
channel.Names(client, sessionRb)
|
channel.Names(client, sessionRb)
|
||||||
}
|
}
|
||||||
sessionRb.Send(false)
|
sessionRb.Send(false)
|
||||||
client.server.cefManager.CEFMessage("POLL", channel.NameCasefolded())
|
channel.RedisBroadcast("VOICEPOLL")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Part parts the given client from this channel, with the given message.
|
// Part parts the given client from this channel, with the given message.
|
||||||
|
|
@ -1459,7 +1459,7 @@ func (channel *Channel) Quit(client *Client) {
|
||||||
client.server.channels.Cleanup(channel)
|
client.server.channels.Cleanup(channel)
|
||||||
}
|
}
|
||||||
client.removeChannel(channel)
|
client.removeChannel(channel)
|
||||||
client.server.cefManager.KickBroadcast(channel.name, client.Nick())
|
channel.Broadcast("KICK", client.NickCasefolded())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (channel *Channel) Kick(client *Client, target *Client, comment string, rb *ResponseBuffer, hasPrivs bool) {
|
func (channel *Channel) Kick(client *Client, target *Client, comment string, rb *ResponseBuffer, hasPrivs bool) {
|
||||||
|
|
|
||||||
|
|
@ -709,6 +709,14 @@ type Config struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
Filename string
|
Filename string
|
||||||
|
|
||||||
|
Cef struct {
|
||||||
|
Imagor struct {
|
||||||
|
Url string
|
||||||
|
Secret string
|
||||||
|
}
|
||||||
|
Redis string
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// OperClass defines an assembled operator class.
|
// OperClass defines an assembled operator class.
|
||||||
|
|
|
||||||
|
|
@ -275,6 +275,6 @@ func (dm *DLineManager) loadFromDatastore() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) loadDLines() {
|
func (server *Server) loadDLines() {
|
||||||
s.dlines = NewDLineManager(s)
|
server.dlines = NewDLineManager(server)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -572,7 +572,7 @@ func batchHandler(server *Server, client *Client, msg ircmsg.Message, rb *Respon
|
||||||
rb.Label = batch.responseLabel
|
rb.Label = batch.responseLabel
|
||||||
|
|
||||||
for _, msg := range batch.message.Split {
|
for _, msg := range batch.message.Split {
|
||||||
signatures := utils.GenerateImagorSignatures(msg.Message)
|
signatures := server.GenerateImagorSignatures(msg.Message)
|
||||||
if len(signatures) > 0 {
|
if len(signatures) > 0 {
|
||||||
if msg.Tags == nil {
|
if msg.Tags == nil {
|
||||||
msg.Tags = make(map[string]string)
|
msg.Tags = make(map[string]string)
|
||||||
|
|
@ -2328,7 +2328,7 @@ func messageHandler(server *Server, client *Client, msg ircmsg.Message, rb *Resp
|
||||||
|
|
||||||
// each target gets distinct msgids
|
// each target gets distinct msgids
|
||||||
splitMsg := utils.MakeMessage(message)
|
splitMsg := utils.MakeMessage(message)
|
||||||
signatures := utils.GenerateImagorSignaturesFromMessage(&msg)
|
signatures := server.GenerateImagorSignaturesFromMessage(&msg)
|
||||||
if len(signatures) > 0 {
|
if len(signatures) > 0 {
|
||||||
if clientOnlyTags == nil {
|
if clientOnlyTags == nil {
|
||||||
clientOnlyTags = make(map[string]string)
|
clientOnlyTags = make(map[string]string)
|
||||||
|
|
@ -2397,7 +2397,7 @@ func dispatchMessageToTarget(client *Client, tags map[string]string, histType hi
|
||||||
for _, mention := range mentions {
|
for _, mention := range mentions {
|
||||||
user := client.server.clients.Get(mention)
|
user := client.server.clients.Get(mention)
|
||||||
if user != nil {
|
if user != nil {
|
||||||
client.server.cefManager.CEFMessage("MENTION", user.nickCasefolded, channel.Name(), message.Msgid)
|
user.RedisBroadcast("MENTION", channel.Name(), message.Msgid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if target[0] == '$' && len(target) > 2 && client.Oper().HasRoleCapab("massmessage") {
|
} else if target[0] == '$' && len(target) > 2 && client.Oper().HasRoleCapab("massmessage") {
|
||||||
|
|
@ -2524,7 +2524,8 @@ func dispatchMessageToTarget(client *Client, tags map[string]string, histType hi
|
||||||
}
|
}
|
||||||
|
|
||||||
client.addHistoryItem(user, item, &details, &tDetails, config)
|
client.addHistoryItem(user, item, &details, &tDetails, config)
|
||||||
client.server.cefManager.CEFMessage("MENTION", user.nickCasefolded, client.nick, message.Msgid)
|
user.RedisBroadcast("MENTION", user.NickCasefolded(), message.Msgid)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -253,6 +253,6 @@ func (km *KLineManager) loadFromDatastore() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) loadKLines() {
|
func (server *Server) loadKLines() {
|
||||||
s.klines = NewKLineManager(s)
|
server.klines = NewKLineManager(server)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,8 @@ import (
|
||||||
"github.com/ergochat/ergo/irc/mysql"
|
"github.com/ergochat/ergo/irc/mysql"
|
||||||
"github.com/ergochat/ergo/irc/sno"
|
"github.com/ergochat/ergo/irc/sno"
|
||||||
"github.com/ergochat/ergo/irc/utils"
|
"github.com/ergochat/ergo/irc/utils"
|
||||||
|
|
||||||
|
"github.com/redis/go-redis/v9"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -98,7 +100,7 @@ type Server struct {
|
||||||
defcon atomic.Uint32
|
defcon atomic.Uint32
|
||||||
|
|
||||||
// CEF
|
// CEF
|
||||||
cefManager *CefConnection
|
redis *redis.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServer returns a new Oragono server.
|
// NewServer returns a new Oragono server.
|
||||||
|
|
@ -166,7 +168,12 @@ func (server *Server) Shutdown() {
|
||||||
func (server *Server) Run() {
|
func (server *Server) Run() {
|
||||||
defer server.Shutdown()
|
defer server.Shutdown()
|
||||||
|
|
||||||
server.cefManager = cefConnection(server)
|
redisOpts, err := redis.ParseURL(server.Config().Cef.Redis)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
server.redis = redis.NewClient(redisOpts)
|
||||||
|
startRedis(server)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,6 @@
|
||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"github.com/cshum/imagor/imagorpath"
|
|
||||||
"github.com/ergochat/irc-go/ircmsg"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
@ -178,78 +170,3 @@ func BuildTokenLines(lineLen int, tokens []string, delim string) []string {
|
||||||
}
|
}
|
||||||
return tl.Lines()
|
return tl.Lines()
|
||||||
}
|
}
|
||||||
|
|
||||||
var urlRegex = regexp.MustCompile("https?:\\/\\/[\\w-]+(\\.[\\w-]+)+([\\w.,@?^=%&:/~+#-]*[\\w@?^=%&/~+#-])?")
|
|
||||||
|
|
||||||
func GenerateImagorSignaturesFromMessage(message *ircmsg.Message) string {
|
|
||||||
line, err := message.Line()
|
|
||||||
if err == nil {
|
|
||||||
return GenerateImagorSignatures(line)
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
var secretKey = os.Getenv("IMAGOR_SECRET")
|
|
||||||
var baseUrl = os.Getenv("IMAGOR_URL")
|
|
||||||
|
|
||||||
func GetUrlMime(url string) string {
|
|
||||||
// hacky, should fix
|
|
||||||
if !strings.Contains(url, "?") {
|
|
||||||
url += "?"
|
|
||||||
}
|
|
||||||
params := imagorpath.Params{
|
|
||||||
Image: url,
|
|
||||||
Meta: true,
|
|
||||||
}
|
|
||||||
metaPath := imagorpath.Generate(params, imagorpath.NewHMACSigner(sha256.New, 0, secretKey))
|
|
||||||
client := http.Client{
|
|
||||||
Timeout: 5 * time.Second,
|
|
||||||
}
|
|
||||||
resp, err := client.Get(baseUrl + metaPath)
|
|
||||||
if err != nil {
|
|
||||||
println("Failed on the initial get")
|
|
||||||
println(err.Error())
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
var meta map[string]interface{}
|
|
||||||
err = json.NewDecoder(resp.Body).Decode(&meta)
|
|
||||||
if err != nil {
|
|
||||||
println("Failed on the JSON decode")
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
contentType, valid := meta["format"].(string)
|
|
||||||
fmt.Printf("%+v\n", meta)
|
|
||||||
if !valid {
|
|
||||||
println("No content type")
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return contentType
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process a message to add Imagor signatures
|
|
||||||
func GenerateImagorSignatures(str string) string {
|
|
||||||
urls := urlRegex.FindAllString(str, -1)
|
|
||||||
var sigs []string
|
|
||||||
for _, url := range urls {
|
|
||||||
params := imagorpath.Params{
|
|
||||||
Image: url,
|
|
||||||
FitIn: true,
|
|
||||||
Width: 600,
|
|
||||||
Height: 600,
|
|
||||||
}
|
|
||||||
path := imagorpath.Generate(params, imagorpath.NewHMACSigner(sha256.New, 0, secretKey))
|
|
||||||
signature := path[:strings.IndexByte(path, '/')]
|
|
||||||
contentType := GetUrlMime(url)
|
|
||||||
if contentType != "" {
|
|
||||||
sigs = append(sigs, signature+"|"+strings.ReplaceAll(contentType, "/", "_"))
|
|
||||||
} else {
|
|
||||||
sigs = append(sigs, signature)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
if len(sigs) > 0 {
|
|
||||||
return strings.Join(sigs, ",")
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
|
||||||
16
vendor/modules.txt
vendored
16
vendor/modules.txt
vendored
|
|
@ -7,9 +7,15 @@ github.com/GehirnInc/crypt
|
||||||
github.com/GehirnInc/crypt/common
|
github.com/GehirnInc/crypt/common
|
||||||
github.com/GehirnInc/crypt/internal
|
github.com/GehirnInc/crypt/internal
|
||||||
github.com/GehirnInc/crypt/md5_crypt
|
github.com/GehirnInc/crypt/md5_crypt
|
||||||
|
# github.com/cespare/xxhash/v2 v2.3.0
|
||||||
|
## explicit; go 1.11
|
||||||
|
github.com/cespare/xxhash/v2
|
||||||
# github.com/cshum/imagor v1.4.13
|
# github.com/cshum/imagor v1.4.13
|
||||||
## explicit; go 1.21
|
## explicit; go 1.21
|
||||||
github.com/cshum/imagor/imagorpath
|
github.com/cshum/imagor/imagorpath
|
||||||
|
# github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f
|
||||||
|
## explicit
|
||||||
|
github.com/dgryski/go-rendezvous
|
||||||
# github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815
|
# github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815
|
||||||
## explicit
|
## explicit
|
||||||
github.com/docopt/docopt-go
|
github.com/docopt/docopt-go
|
||||||
|
|
@ -46,6 +52,16 @@ github.com/okzk/sdnotify
|
||||||
## explicit; go 1.12
|
## explicit; go 1.12
|
||||||
# github.com/onsi/gomega v1.9.0
|
# github.com/onsi/gomega v1.9.0
|
||||||
## explicit
|
## explicit
|
||||||
|
# github.com/redis/go-redis/v9 v9.6.1
|
||||||
|
## explicit; go 1.18
|
||||||
|
github.com/redis/go-redis/v9
|
||||||
|
github.com/redis/go-redis/v9/internal
|
||||||
|
github.com/redis/go-redis/v9/internal/hashtag
|
||||||
|
github.com/redis/go-redis/v9/internal/hscan
|
||||||
|
github.com/redis/go-redis/v9/internal/pool
|
||||||
|
github.com/redis/go-redis/v9/internal/proto
|
||||||
|
github.com/redis/go-redis/v9/internal/rand
|
||||||
|
github.com/redis/go-redis/v9/internal/util
|
||||||
# github.com/tidwall/btree v1.4.2
|
# github.com/tidwall/btree v1.4.2
|
||||||
## explicit; go 1.18
|
## explicit; go 1.18
|
||||||
github.com/tidwall/btree
|
github.com/tidwall/btree
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue