mirror of
https://github.com/42wim/matterbridge.git
synced 2026-01-01 07:58:09 +01:00
Merge 0cd8db8a55eb5197e12954a66b3739fedfa908b6 into 56e7bd01ca09ad52b0c4f48f146a20a4f1b78696
This commit is contained in:
commit
f76341db1b
@ -80,33 +80,52 @@ func (b *Birc) handleInvite(client *girc.Client, event girc.Event) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isKill(quitmsg string) bool {
|
||||||
|
return (strings.HasPrefix(quitmsg, "Killed") ||
|
||||||
|
strings.HasPrefix(quitmsg, "Local kill") ||
|
||||||
|
(len(quitmsg) > 7 && strings.HasPrefix(quitmsg[1:], "-lined")))
|
||||||
|
}
|
||||||
|
|
||||||
func (b *Birc) handleJoinPart(client *girc.Client, event girc.Event) {
|
func (b *Birc) handleJoinPart(client *girc.Client, event girc.Event) {
|
||||||
if len(event.Params) == 0 {
|
if len(event.Params) == 0 {
|
||||||
b.Log.Debugf("handleJoinPart: empty Params? %#v", event)
|
b.Log.Debugf("handleJoinPart: empty Params? %#v", event)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
channel := strings.ToLower(event.Params[0])
|
channel := strings.ToLower(event.Params[0])
|
||||||
if event.Command == "KICK" && event.Params[1] == b.Nick {
|
if event.Command == "KICK" {
|
||||||
b.Log.Infof("Got kicked from %s by %s", channel, event.Source.Name)
|
if event.Params[1] == b.Nick {
|
||||||
time.Sleep(time.Duration(b.GetInt("RejoinDelay")) * time.Second)
|
b.Log.Infof("Got kicked from %s by %s", channel, event.Source.Name)
|
||||||
b.Remote <- config.Message{Username: "system", Text: "rejoin", Channel: channel, Account: b.Account, Event: config.EventRejoinChannels}
|
time.Sleep(time.Duration(b.GetInt("RejoinDelay")) * time.Second)
|
||||||
return
|
b.Remote <- config.Message{Username: "system", Text: "rejoin", Channel: channel, Account: b.Account, Event: config.EventRejoinChannels}
|
||||||
}
|
|
||||||
if event.Command == "QUIT" {
|
|
||||||
if event.Source.Name == b.Nick && strings.Contains(event.Last(), "Ping timeout") {
|
|
||||||
b.Log.Infof("%s reconnecting ..", b.Account)
|
|
||||||
b.Remote <- config.Message{Username: "system", Text: "reconnect", Channel: channel, Account: b.Account, Event: config.EventFailure}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if event.Source.Name != b.Nick {
|
|
||||||
if b.GetBool("nosendjoinpart") {
|
if b.GetBool("nosendjoinpart") {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
msg := config.Message{Username: "system", Text: event.Source.Name + " " + strings.ToLower(event.Command) + "s", Channel: channel, Account: b.Account, Event: config.EventJoinLeave}
|
msg := config.Message{
|
||||||
|
Username: "system",
|
||||||
|
Text: event.Source.Name + " kicked " + event.Params[1] + " with message: " + event.Last(),
|
||||||
|
Channel: channel,
|
||||||
|
Account: b.Account,
|
||||||
|
Event: config.EventJoinLeave,
|
||||||
|
}
|
||||||
|
b.Log.Debugf("<= Message is %#v", msg)
|
||||||
|
b.Remote <- msg
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if event.Source.Name != b.Nick {
|
||||||
|
if isActive, _ := b.isUserActive(event.Source.Name, channel); !isActive ||
|
||||||
|
b.GetBool("nosendjoinpart") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
partmsg := ""
|
||||||
|
if event.Command == "PART" && len(event.Params) >= 2 {
|
||||||
|
partmsg = " with message: " + event.Last()
|
||||||
|
}
|
||||||
|
msg := config.Message{Username: "system", Text: event.Source.Name + " " + strings.ToLower(event.Command) + "s" + partmsg, Channel: channel, Account: b.Account, Event: config.EventJoinLeave}
|
||||||
if b.GetBool("verbosejoinpart") {
|
if b.GetBool("verbosejoinpart") {
|
||||||
b.Log.Debugf("<= Sending verbose JOIN_LEAVE event from %s to gateway", b.Account)
|
b.Log.Debugf("<= Sending verbose JOIN_LEAVE event from %s to gateway", b.Account)
|
||||||
msg = config.Message{Username: "system", Text: event.Source.Name + " (" + event.Source.Ident + "@" + event.Source.Host + ") " + strings.ToLower(event.Command) + "s", Channel: channel, Account: b.Account, Event: config.EventJoinLeave}
|
msg = config.Message{Username: "system", Text: event.Source.Name + " (" + event.Source.Ident + "@" + event.Source.Host + ") " + strings.ToLower(event.Command) + "s" + partmsg, Channel: channel, Account: b.Account, Event: config.EventJoinLeave}
|
||||||
} else {
|
} else {
|
||||||
b.Log.Debugf("<= Sending JOIN_LEAVE event from %s to gateway", b.Account)
|
b.Log.Debugf("<= Sending JOIN_LEAVE event from %s to gateway", b.Account)
|
||||||
}
|
}
|
||||||
@ -122,15 +141,43 @@ func (b *Birc) handleNewConnection(client *girc.Client, event girc.Event) {
|
|||||||
i := b.i
|
i := b.i
|
||||||
b.Nick = event.Params[0]
|
b.Nick = event.Params[0]
|
||||||
|
|
||||||
i.Handlers.AddBg("PRIVMSG", b.handlePrivMsg)
|
i.Handlers.Add("INVITE", b.handleInvite)
|
||||||
i.Handlers.AddBg("CTCP_ACTION", b.handlePrivMsg)
|
|
||||||
i.Handlers.Add(girc.RPL_TOPICWHOTIME, b.handleTopicWhoTime)
|
|
||||||
i.Handlers.AddBg(girc.NOTICE, b.handleNotice)
|
|
||||||
i.Handlers.AddBg("JOIN", b.handleJoinPart)
|
i.Handlers.AddBg("JOIN", b.handleJoinPart)
|
||||||
i.Handlers.AddBg("PART", b.handleJoinPart)
|
i.Handlers.AddBg("PART", b.handleJoinPart)
|
||||||
i.Handlers.AddBg("QUIT", b.handleJoinPart)
|
|
||||||
i.Handlers.AddBg("KICK", b.handleJoinPart)
|
i.Handlers.AddBg("KICK", b.handleJoinPart)
|
||||||
i.Handlers.Add("INVITE", b.handleInvite)
|
i.Handlers.AddBg("NICK", b.handleNick)
|
||||||
|
i.Handlers.AddBg(girc.NOTICE, b.handleNotice)
|
||||||
|
i.Handlers.AddBg("PRIVMSG", b.handlePrivMsg)
|
||||||
|
i.Handlers.AddBg("CTCP_ACTION", b.handlePrivMsg)
|
||||||
|
i.Handlers.AddBg("QUIT", b.handleQuit)
|
||||||
|
i.Handlers.Add(girc.RPL_TOPICWHOTIME, b.handleTopicWhoTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Birc) handleNick(client *girc.Client, event girc.Event) {
|
||||||
|
if len(event.Params) != 1 {
|
||||||
|
b.Log.Debugf("handleJoinPart: malformed nick change? %#v", event)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if b.GetBool("nosendjoinpart") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if activeChannels := b.getActiveChannels(event.Source.Name); len(activeChannels) > 0 {
|
||||||
|
for _, activityInfo := range activeChannels {
|
||||||
|
msg := config.Message{
|
||||||
|
Username: "system",
|
||||||
|
Text: event.Source.Name + " changed nick to " + event.Params[0],
|
||||||
|
Channel: activityInfo.channel,
|
||||||
|
Account: b.Account,
|
||||||
|
Event: config.EventJoinLeave,
|
||||||
|
}
|
||||||
|
b.Log.Debugf("<= Message is %#v", msg)
|
||||||
|
b.Remote <- msg
|
||||||
|
if b.ActivityTimeout != 0 {
|
||||||
|
// This doesn't count as new activity, but it does preserve the value
|
||||||
|
b.markUserActive(event.Params[0], activityInfo.channel, activityInfo.activeTime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Birc) handleNickServ() {
|
func (b *Birc) handleNickServ() {
|
||||||
@ -185,9 +232,10 @@ func (b *Birc) handlePrivMsg(client *girc.Client, event girc.Event) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
channel := strings.ToLower(event.Params[0])
|
||||||
rmsg := config.Message{
|
rmsg := config.Message{
|
||||||
Username: event.Source.Name,
|
Username: event.Source.Name,
|
||||||
Channel: strings.ToLower(event.Params[0]),
|
Channel: channel,
|
||||||
Account: b.Account,
|
Account: b.Account,
|
||||||
UserID: event.Source.Ident + "@" + event.Source.Host,
|
UserID: event.Source.Ident + "@" + event.Source.Host,
|
||||||
}
|
}
|
||||||
@ -238,7 +286,41 @@ func (b *Birc) handlePrivMsg(client *girc.Client, event girc.Event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
b.Log.Debugf("<= Sending message from %s on %s to gateway", event.Params[0], b.Account)
|
b.Log.Debugf("<= Sending message from %s on %s to gateway", event.Params[0], b.Account)
|
||||||
|
if b.ActivityTimeout > 0 {
|
||||||
|
b.markUserActive(event.Source.Name, channel, time.Now().Unix())
|
||||||
|
}
|
||||||
b.Remote <- rmsg
|
b.Remote <- rmsg
|
||||||
|
b.cleanActiveMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Birc) handleQuit(client *girc.Client, event girc.Event) {
|
||||||
|
if event.Source.Name == b.Nick && strings.Contains(event.Last(), "Ping timeout") {
|
||||||
|
b.Log.Infof("%s reconnecting ..", b.Account)
|
||||||
|
for mychan := range b.channels {
|
||||||
|
b.Remote <- config.Message{Username: "system", Text: "reconnect", Channel: mychan, Account: b.Account, Event: config.EventFailure}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
} else if b.GetBool("nosendjoinpart") {
|
||||||
|
return
|
||||||
|
} else if activeChannels, found := b.activeUsers[event.Source.Name]; found {
|
||||||
|
userWasKilled := isKill(event.Last())
|
||||||
|
verbosequit := ""
|
||||||
|
quitmsg := ""
|
||||||
|
nowTime := time.Now().Unix()
|
||||||
|
if len(event.Params) >= 1 {
|
||||||
|
quitmsg = " with message: " + event.Last()
|
||||||
|
}
|
||||||
|
if b.GetBool("verbosejoinpart") {
|
||||||
|
verbosequit = " (" + event.Source.Ident + "@" + event.Source.Host + ")"
|
||||||
|
}
|
||||||
|
for channel, activeTime := range activeChannels {
|
||||||
|
if userWasKilled || b.isActive(activeTime, nowTime) {
|
||||||
|
msg := config.Message{Username: "system", Text: event.Source.Name + verbosequit + " quit" + quitmsg, Channel: channel, Account: b.Account, Event: config.EventJoinLeave}
|
||||||
|
b.Log.Debugf("<= Message is %#v", msg)
|
||||||
|
b.Remote <- msg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Birc) handleRunCommands() {
|
func (b *Birc) handleRunCommands() {
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/42wim/matterbridge/bridge"
|
"github.com/42wim/matterbridge/bridge"
|
||||||
@ -27,20 +28,30 @@ type Birc struct {
|
|||||||
i *girc.Client
|
i *girc.Client
|
||||||
Nick string
|
Nick string
|
||||||
names map[string][]string
|
names map[string][]string
|
||||||
|
activeUsers map[string]map[string]int64
|
||||||
|
activeUsersLastCleaned int64
|
||||||
|
activeUsersMutex sync.RWMutex
|
||||||
connected chan error
|
connected chan error
|
||||||
Local chan config.Message // local queue for flood control
|
Local chan config.Message // local queue for flood control
|
||||||
FirstConnection, authDone bool
|
FirstConnection, authDone bool
|
||||||
MessageDelay, MessageQueue, MessageLength int
|
MessageDelay, MessageQueue, MessageLength int
|
||||||
|
ActivityTimeout int64
|
||||||
channels map[string]bool
|
channels map[string]bool
|
||||||
|
|
||||||
*bridge.Config
|
*bridge.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ActivityInfo struct {
|
||||||
|
channel string
|
||||||
|
activeTime int64
|
||||||
|
}
|
||||||
|
|
||||||
func New(cfg *bridge.Config) bridge.Bridger {
|
func New(cfg *bridge.Config) bridge.Bridger {
|
||||||
b := &Birc{}
|
b := &Birc{}
|
||||||
b.Config = cfg
|
b.Config = cfg
|
||||||
b.Nick = b.GetString("Nick")
|
b.Nick = b.GetString("Nick")
|
||||||
b.names = make(map[string][]string)
|
b.names = make(map[string][]string)
|
||||||
|
b.activeUsers = make(map[string]map[string]int64)
|
||||||
b.connected = make(chan error)
|
b.connected = make(chan error)
|
||||||
b.channels = make(map[string]bool)
|
b.channels = make(map[string]bool)
|
||||||
|
|
||||||
@ -59,6 +70,16 @@ func New(cfg *bridge.Config) bridge.Bridger {
|
|||||||
} else {
|
} else {
|
||||||
b.MessageLength = b.GetInt("MessageLength")
|
b.MessageLength = b.GetInt("MessageLength")
|
||||||
}
|
}
|
||||||
|
if b.GetBool("ShowActiveUserEvents") {
|
||||||
|
if b.GetInt("ActivityTimeout") == 0 {
|
||||||
|
b.ActivityTimeout = 1800 // 30 minutes
|
||||||
|
} else {
|
||||||
|
b.ActivityTimeout = int64(b.GetInt("ActivityTimeout"))
|
||||||
|
}
|
||||||
|
b.activeUsersLastCleaned = time.Now().Unix()
|
||||||
|
} else {
|
||||||
|
b.ActivityTimeout = 0 // Disable
|
||||||
|
}
|
||||||
b.FirstConnection = true
|
b.FirstConnection = true
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
@ -77,6 +98,10 @@ func (b *Birc) Connect() error {
|
|||||||
return errors.New("you can't enable SASL and TLSClientCertificate at the same time")
|
return errors.New("you can't enable SASL and TLSClientCertificate at the same time")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if b.GetBool("NoSendJoinPart") && b.GetBool("ShowActiveUserEvents") {
|
||||||
|
return errors.New("you must disable NoSendJoinPart to use ShowActiveUserEvents")
|
||||||
|
}
|
||||||
|
|
||||||
b.Local = make(chan config.Message, b.MessageQueue+10)
|
b.Local = make(chan config.Message, b.MessageQueue+10)
|
||||||
b.Log.Infof("Connecting %s", b.GetString("Server"))
|
b.Log.Infof("Connecting %s", b.GetString("Server"))
|
||||||
|
|
||||||
@ -413,3 +438,76 @@ func (b *Birc) getTLSConfig() (*tls.Config, error) {
|
|||||||
|
|
||||||
return tlsConfig, nil
|
return tlsConfig, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *Birc) isActive(activityTime int64, nowTime int64) bool {
|
||||||
|
return (nowTime - activityTime) < b.ActivityTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Birc) isUserActive(nick string, channel string) (bool, int64) {
|
||||||
|
b.Log.Debugf("checking activity for %s", nick)
|
||||||
|
if b.ActivityTimeout == 0 {
|
||||||
|
return true, 0
|
||||||
|
}
|
||||||
|
b.activeUsersMutex.RLock()
|
||||||
|
defer b.activeUsersMutex.RUnlock()
|
||||||
|
if activeTime, ok := b.activeUsers[nick][channel]; ok {
|
||||||
|
now := time.Now().Unix()
|
||||||
|
b.Log.Debugf("last activity for %s was %d, currently %d", nick, activeTime, now)
|
||||||
|
if now < activeTime {
|
||||||
|
b.Log.Errorf("User %s has active time in the future: %d", nick, activeTime)
|
||||||
|
return true, now // err on the side of caution
|
||||||
|
}
|
||||||
|
return b.isActive(activeTime, now), activeTime
|
||||||
|
}
|
||||||
|
return false, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Birc) getActiveChannels(nick string) []ActivityInfo {
|
||||||
|
retval := make([]ActivityInfo, 0)
|
||||||
|
if channels, found := b.activeUsers[nick]; found {
|
||||||
|
now := time.Now().Unix()
|
||||||
|
for channel, activeTime := range channels {
|
||||||
|
if now < activeTime {
|
||||||
|
b.Log.Errorf("User %s has active time for channel %s in the future: %d",
|
||||||
|
nick,
|
||||||
|
channel,
|
||||||
|
activeTime)
|
||||||
|
} else if (now - activeTime) < b.ActivityTimeout {
|
||||||
|
retval = append(retval, ActivityInfo{channel, activeTime})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retval
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Birc) cleanActiveMap() {
|
||||||
|
now := time.Now().Unix()
|
||||||
|
if b.ActivityTimeout == 0 || (b.activeUsersLastCleaned-now < b.ActivityTimeout) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b.activeUsersMutex.Lock()
|
||||||
|
defer b.activeUsersMutex.Unlock()
|
||||||
|
for nick, activeChannels := range b.activeUsers {
|
||||||
|
for channel, activeTime := range activeChannels {
|
||||||
|
if now-activeTime > b.ActivityTimeout {
|
||||||
|
b.Log.Debugf("last activity for %s was %d, currently %d. Deleting.", nick, activeTime, now)
|
||||||
|
delete(activeChannels, channel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if 0 == len(activeChannels) {
|
||||||
|
delete(b.activeUsers, nick)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Birc) markUserActive(nick string, channel string, activeTime int64) {
|
||||||
|
b.Log.Debugf("<= Updating last-active time for user %s in channel %s to %d", nick, channel, activeTime)
|
||||||
|
b.activeUsersMutex.Lock()
|
||||||
|
defer b.activeUsersMutex.Unlock()
|
||||||
|
nickActivity, found := b.activeUsers[nick]
|
||||||
|
if !found {
|
||||||
|
b.activeUsers[nick] = make(map[string]int64)
|
||||||
|
nickActivity = b.activeUsers[nick]
|
||||||
|
}
|
||||||
|
nickActivity[channel] = activeTime
|
||||||
|
}
|
||||||
|
|||||||
@ -192,6 +192,14 @@ RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
|||||||
#OPTIONAL (default false)
|
#OPTIONAL (default false)
|
||||||
ShowJoinPart=false
|
ShowJoinPart=false
|
||||||
|
|
||||||
|
#Only show join/part/quit information for users who have been active recently.
|
||||||
|
#OPTIONAL (default false)
|
||||||
|
ShowActiveUserEvents=false
|
||||||
|
|
||||||
|
#A user is considered active for ShowActiveUserEvents if they've spoken within this number of seconds.
|
||||||
|
#OPTIONAL (default 1800, which is 30 minutes)
|
||||||
|
ActivityTimeout=1800
|
||||||
|
|
||||||
#Enable to show verbose users joins/parts (ident@host) from other bridges
|
#Enable to show verbose users joins/parts (ident@host) from other bridges
|
||||||
#Currently works for messages from the following bridges: irc
|
#Currently works for messages from the following bridges: irc
|
||||||
#OPTIONAL (default false)
|
#OPTIONAL (default false)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user