forked from hswaw/hscloud
personal/q3k/lelegram: changes by zagura
* Log high verbose debug messages * New cli parameter irc_login * Change regex for IRC nicks * IRC channel names case insensitive * IRC usernames truncated to 9 chars without Telegram suffix Signed-off-by: Michał Zagórski <zagura6@gmail.com> Change-Id: Ifa32279580a4378cc3b9e255f0311216998e02c9master
parent
e186c87c1b
commit
5b1aa134fe
|
@ -53,7 +53,7 @@ type ircconn struct {
|
||||||
connected int64
|
connected int64
|
||||||
}
|
}
|
||||||
|
|
||||||
var reIRCNick = regexp.MustCompile(`[^A-Za-z0-09]`)
|
var reIRCNick = regexp.MustCompile(`[^A-Za-z0-9]`)
|
||||||
|
|
||||||
// Say is called by the Manager when a message should be sent out by the
|
// Say is called by the Manager when a message should be sent out by the
|
||||||
// connection.
|
// connection.
|
||||||
|
@ -74,8 +74,23 @@ type IRCMessage struct {
|
||||||
text string
|
text string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConn(server, channel, user string, backup bool, h func(e *event)) (*ircconn, error) {
|
func NewConn(server, channel, userTelegram string, backup bool, h func(e *event)) (*ircconn, error) {
|
||||||
glog.Infof("Connecting to IRC/%s/%s/%s...", server, channel, user)
|
// Generate IRC nick from username.
|
||||||
|
nick := reIRCNick.ReplaceAllString(userTelegram, "")
|
||||||
|
username := nick
|
||||||
|
if len(username) > 9 {
|
||||||
|
username = username[:9]
|
||||||
|
}
|
||||||
|
nick = strings.ToLower(nick)
|
||||||
|
if len(nick) > 13 {
|
||||||
|
nick = nick[:13]
|
||||||
|
}
|
||||||
|
if len(nick) == 0 {
|
||||||
|
glog.Errorf("Could not create IRC nick for %q", userTelegram)
|
||||||
|
nick = "wtf"
|
||||||
|
}
|
||||||
|
nick += "[t]"
|
||||||
|
glog.Infof("Connecting to IRC/%s/%s/%s as %s from %s...", server, channel, userTelegram, nick, username)
|
||||||
conn, err := net.Dial("tcp", server)
|
conn, err := net.Dial("tcp", server)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Dial(_, %q): %v", server, err)
|
return nil, fmt.Errorf("Dial(_, %q): %v", server, err)
|
||||||
|
@ -84,7 +99,7 @@ func NewConn(server, channel, user string, backup bool, h func(e *event)) (*ircc
|
||||||
i := &ircconn{
|
i := &ircconn{
|
||||||
server: server,
|
server: server,
|
||||||
channel: channel,
|
channel: channel,
|
||||||
user: user,
|
user: userTelegram,
|
||||||
|
|
||||||
eventHandler: h,
|
eventHandler: h,
|
||||||
|
|
||||||
|
@ -102,22 +117,14 @@ func NewConn(server, channel, user string, backup bool, h func(e *event)) (*ircc
|
||||||
connected: int64(0),
|
connected: int64(0),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate IRC nick from username.
|
|
||||||
nick := reIRCNick.ReplaceAllString(user, "")
|
|
||||||
if len(nick) > 13 {
|
|
||||||
nick = nick[:13]
|
|
||||||
}
|
|
||||||
if len(nick) == 0 {
|
|
||||||
glog.Errorf("Could not create IRC nick for %q", user)
|
|
||||||
nick = "wtf"
|
|
||||||
}
|
|
||||||
nick += "[t]"
|
|
||||||
|
|
||||||
// Configure IRC client to populate the IRC Queue.
|
// Configure IRC client to populate the IRC Queue.
|
||||||
config := irc.ClientConfig{
|
config := irc.ClientConfig{
|
||||||
Nick: nick,
|
Nick: nick,
|
||||||
User: user,
|
User: username,
|
||||||
Name: user,
|
Name: userTelegram,
|
||||||
Handler: irc.HandlerFunc(func(c *irc.Client, m *irc.Message) {
|
Handler: irc.HandlerFunc(func(c *irc.Client, m *irc.Message) {
|
||||||
i.iq <- m
|
i.iq <- m
|
||||||
}),
|
}),
|
||||||
|
@ -221,6 +228,9 @@ func (i *ircconn) loop(ctx context.Context) {
|
||||||
glog.V(1).Infof("IRC/%s/debug: %+v", i.user, m)
|
glog.V(1).Infof("IRC/%s/debug: %+v", i.user, m)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
glog.V(16).Infof("irc/debug16: Message: cmd(%s), channel(%s)", m.Command, m.Params[0])
|
||||||
|
glog.V(16).Infof("irc/debug16: Current: channel(%s), command(%s)", i.channel, "PRIVMSG")
|
||||||
|
glog.V(16).Infof("irc/debug16: Current: channel-eq(%t), command-eq(%t)", i.channel == m.Params[0], "PRIVMSG" == m.Command)
|
||||||
switch {
|
switch {
|
||||||
case m.Command == "001":
|
case m.Command == "001":
|
||||||
glog.Infof("IRC/%s/info: joining %s...", i.user, i.channel)
|
glog.Infof("IRC/%s/info: joining %s...", i.user, i.channel)
|
||||||
|
@ -250,8 +260,8 @@ func (i *ircconn) loop(ctx context.Context) {
|
||||||
glog.Infof("IRC/%s/info: got kicked", i.user)
|
glog.Infof("IRC/%s/info: got kicked", i.user)
|
||||||
die(nil)
|
die(nil)
|
||||||
return
|
return
|
||||||
|
case m.Command == "PRIVMSG" && strings.ToLower(m.Params[0]) == i.channel:
|
||||||
case m.Command == "PRIVMSG" && m.Params[0] == i.channel:
|
glog.V(8).Infof("IRC/%s/debug8: received message on %s", i.user, i.channel)
|
||||||
go i.eventHandler(&event{
|
go i.eventHandler(&event{
|
||||||
message: &eventMessage{i, m.Prefix.Name, m.Params[1]},
|
message: &eventMessage{i, m.Prefix.Name, m.Params[1]},
|
||||||
})
|
})
|
||||||
|
|
|
@ -24,6 +24,8 @@ import (
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
// maximum IRC sessions to maintain
|
// maximum IRC sessions to maintain
|
||||||
max int
|
max int
|
||||||
|
// IRC bot's username
|
||||||
|
login string
|
||||||
// IRC server address
|
// IRC server address
|
||||||
server string
|
server string
|
||||||
// IRC channel name
|
// IRC channel name
|
||||||
|
@ -45,9 +47,10 @@ type Manager struct {
|
||||||
runctx context.Context
|
runctx context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewManager(max int, server, channel string) *Manager {
|
func NewManager(max int, server, channel string, login string) *Manager {
|
||||||
return &Manager{
|
return &Manager{
|
||||||
max: max,
|
max: max,
|
||||||
|
login: login,
|
||||||
server: server,
|
server: server,
|
||||||
channel: channel,
|
channel: channel,
|
||||||
ctrl: make(chan *control),
|
ctrl: make(chan *control),
|
||||||
|
@ -142,7 +145,7 @@ func (m *Manager) ensureReceiver(ctx context.Context) {
|
||||||
if len(m.conns) == 0 {
|
if len(m.conns) == 0 {
|
||||||
// Noone said anything on telegram, make backup
|
// Noone said anything on telegram, make backup
|
||||||
glog.Infof("No receiver found, making backup")
|
glog.Infof("No receiver found, making backup")
|
||||||
name := "lelegram"
|
name := m.login
|
||||||
c, err := m.newconn(ctx, name, true)
|
c, err := m.newconn(ctx, name, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Errorf("Could not make backup receiver: %v", err)
|
glog.Errorf("Could not make backup receiver: %v", err)
|
||||||
|
|
|
@ -14,13 +14,13 @@ var (
|
||||||
|
|
||||||
// getconn either gets a connection by username, or creates a new one (after
|
// getconn either gets a connection by username, or creates a new one (after
|
||||||
// evicting the least recently used connection).
|
// evicting the least recently used connection).
|
||||||
func (m *Manager) getconn(ctx context.Context, user string) (*ircconn, error) {
|
func (m *Manager) getconn(ctx context.Context, userTelegram string) (*ircconn, error) {
|
||||||
// Is the user shitlisted?
|
// Is the user shitlisted?
|
||||||
if t, ok := m.shitlist[user]; ok && time.Now().Before(t) {
|
if t, ok := m.shitlist[userTelegram]; ok && time.Now().Before(t) {
|
||||||
return nil, errBanned
|
return nil, errBanned
|
||||||
}
|
}
|
||||||
// Do we already have a connection?
|
// Do we already have a connection?
|
||||||
c, ok := m.conns[user]
|
c, ok := m.conns[userTelegram]
|
||||||
if ok {
|
if ok {
|
||||||
// Bump and return.
|
// Bump and return.
|
||||||
c.last = time.Now()
|
c.last = time.Now()
|
||||||
|
@ -46,17 +46,17 @@ func (m *Manager) getconn(ctx context.Context, user string) (*ircconn, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate new connection
|
// Allocate new connection
|
||||||
return m.newconn(ctx, user, false)
|
return m.newconn(ctx, userTelegram, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// newconn creates a new IRC connection as a given user, and saves it to the
|
// newconn creates a new IRC connection as a given user, and saves it to the
|
||||||
// conns map.
|
// conns map.
|
||||||
func (m *Manager) newconn(ctx context.Context, user string, backup bool) (*ircconn, error) {
|
func (m *Manager) newconn(ctx context.Context, userTelegram string, backup bool) (*ircconn, error) {
|
||||||
c, err := NewConn(m.server, m.channel, user, backup, m.Event)
|
c, err := NewConn(m.server, m.channel, userTelegram, backup, m.Event)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
m.conns[user] = c
|
m.conns[userTelegram] = c
|
||||||
|
|
||||||
go c.Run(m.runctx)
|
go c.Run(m.runctx)
|
||||||
|
|
||||||
|
|
|
@ -77,8 +77,10 @@ func (m *Manager) notifyAll(n *Notification) {
|
||||||
|
|
||||||
// doevent handles incoming events.
|
// doevent handles incoming events.
|
||||||
func (m *Manager) doevent(ctx context.Context, e *event) {
|
func (m *Manager) doevent(ctx context.Context, e *event) {
|
||||||
|
glog.V(16).Infof("event/debug16: New event handling")
|
||||||
switch {
|
switch {
|
||||||
case e.nick != nil:
|
case e.nick != nil:
|
||||||
|
glog.V(8).Infof("event/debug8: nick (%s)", e.nick.nick)
|
||||||
// Nick update from connection
|
// Nick update from connection
|
||||||
|
|
||||||
// Ensure this connection is still used.
|
// Ensure this connection is still used.
|
||||||
|
|
|
@ -27,6 +27,7 @@ var (
|
||||||
flagIRCMaxConnections int
|
flagIRCMaxConnections int
|
||||||
flagIRCServer string
|
flagIRCServer string
|
||||||
flagIRCChannel string
|
flagIRCChannel string
|
||||||
|
flagIRCLogin string
|
||||||
)
|
)
|
||||||
|
|
||||||
// server is responsible for briding IRC and Telegram.
|
// server is responsible for briding IRC and Telegram.
|
||||||
|
@ -76,6 +77,7 @@ func main() {
|
||||||
flag.IntVar(&flagIRCMaxConnections, "irc_max_connections", 10, "How many simulataneous connections can there be to IRC before they get recycled")
|
flag.IntVar(&flagIRCMaxConnections, "irc_max_connections", 10, "How many simulataneous connections can there be to IRC before they get recycled")
|
||||||
flag.StringVar(&flagIRCServer, "irc_server", "chat.freenode.net:6667", "The address (with port) of the IRC server to connect to")
|
flag.StringVar(&flagIRCServer, "irc_server", "chat.freenode.net:6667", "The address (with port) of the IRC server to connect to")
|
||||||
flag.StringVar(&flagIRCChannel, "irc_channel", "", "The channel name (including hash(es)) to bridge")
|
flag.StringVar(&flagIRCChannel, "irc_channel", "", "The channel name (including hash(es)) to bridge")
|
||||||
|
flag.StringVar(&flagIRCLogin, "irc_login", "lelegram[t]", "The login of irc user used by bot")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
if flagTelegramToken == "" {
|
if flagTelegramToken == "" {
|
||||||
|
@ -85,7 +87,10 @@ func main() {
|
||||||
if flagIRCChannel == "" {
|
if flagIRCChannel == "" {
|
||||||
glog.Exitf("irc_channel must be set")
|
glog.Exitf("irc_channel must be set")
|
||||||
}
|
}
|
||||||
|
if flagIRCLogin == "" {
|
||||||
|
flagIRCLogin = "lelegram"
|
||||||
|
}
|
||||||
|
glog.Infof("dabug: Backup login in IRC: %s", flagIRCLogin)
|
||||||
// Parse given group ID.
|
// Parse given group ID.
|
||||||
// If not set, start server in 'lame' mode, ie. one that will not actually
|
// If not set, start server in 'lame' mode, ie. one that will not actually
|
||||||
// perform any bridging, but will let you figure out the IDs of groups that
|
// perform any bridging, but will let you figure out the IDs of groups that
|
||||||
|
@ -107,8 +112,9 @@ func main() {
|
||||||
glog.Exitf("Listen(): %v", err)
|
glog.Exitf("Listen(): %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
mgr := irc.NewManager(flagIRCMaxConnections, flagIRCServer, flagIRCChannel)
|
// https://tools.ietf.org/html/rfc2812#section-1.3 "Channel names are case insensitive"
|
||||||
|
mgr := irc.NewManager(flagIRCMaxConnections, flagIRCServer, strings.ToLower(flagIRCChannel), flagIRCLogin)
|
||||||
|
glog.V(4).Infof("telegram/debug4: Linking to group: %d", groupId)
|
||||||
s, err := newServer(groupId, mgr)
|
s, err := newServer(groupId, mgr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Exitf("newServer(): %v", err)
|
glog.Exitf("newServer(): %v", err)
|
||||||
|
@ -140,17 +146,17 @@ func main() {
|
||||||
func (s *server) bridge(ctx context.Context) {
|
func (s *server) bridge(ctx context.Context) {
|
||||||
nickmap := make(map[string]string)
|
nickmap := make(map[string]string)
|
||||||
for {
|
for {
|
||||||
|
glog.V(32).Info("bridge/debug32: New element in queue")
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return
|
return
|
||||||
|
|
||||||
case m := <-s.telLog:
|
case m := <-s.telLog:
|
||||||
// Event from Telegram (message). Translate Telegram names into IRC names.
|
// Event from Telegram (message). Translate Telegram names into IRC names.
|
||||||
text := m.text
|
text := m.text
|
||||||
for t, i := range nickmap {
|
for t, i := range nickmap {
|
||||||
text = strings.ReplaceAll(text, "@"+t, i)
|
text = strings.ReplaceAll(text, "@"+t, i)
|
||||||
}
|
}
|
||||||
glog.Infof("telegram/%s: %v", m.user, text)
|
glog.Infof("telegram/info/%s: %v", m.user, text)
|
||||||
|
|
||||||
// Attempt to route message to IRC twice.
|
// Attempt to route message to IRC twice.
|
||||||
// This blocks until success or failure, making sure the log stays
|
// This blocks until success or failure, making sure the log stays
|
||||||
|
@ -166,6 +172,7 @@ func (s *server) bridge(ctx context.Context) {
|
||||||
cancel()
|
cancel()
|
||||||
|
|
||||||
case n := <-s.ircLog:
|
case n := <-s.ircLog:
|
||||||
|
glog.V(4).Infof("bridge/irc/debug4: Get message from irc: %s", n.Message)
|
||||||
// Notification from IRC (message or new nickmap)
|
// Notification from IRC (message or new nickmap)
|
||||||
switch {
|
switch {
|
||||||
case n.Nickmap != nil:
|
case n.Nickmap != nil:
|
||||||
|
|
|
@ -21,29 +21,31 @@ func (s *server) telegramConnection(ctx context.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("GetUpdatesChan(%+v): %v", u, err)
|
return fmt.Errorf("GetUpdatesChan(%+v): %v", u, err)
|
||||||
}
|
}
|
||||||
|
glog.V(8).Infof("telegram/debug8: New update")
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return ctx.Err()
|
return ctx.Err()
|
||||||
case u, ok := <-updates:
|
case update, ok := <-updates:
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("Updates channel closed")
|
return fmt.Errorf("Updates channel closed")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dispatch update.
|
// Dispatch update.
|
||||||
switch {
|
switch {
|
||||||
case u.Message != nil:
|
case update.Message != nil:
|
||||||
if u.Message.Chat.ID != s.groupId {
|
glog.V(4).Infof("telegram/debug4: New message: %d", update.Message.Chat.ID)
|
||||||
glog.Infof("[ignored group %d] <%s> %v", u.Message.Chat.ID, u.Message.From, u.Message.Text)
|
if update.Message.Chat.ID != s.groupId {
|
||||||
|
glog.Infof("[ignored group %d] <%s> %v", update.Message.Chat.ID, update.Message.From, update.Message.Text)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
date := time.Unix(int64(u.Message.Date), 0)
|
date := time.Unix(int64(update.Message.Date), 0)
|
||||||
if time.Since(date) > 2*time.Minute {
|
if time.Since(date) > 2*time.Minute {
|
||||||
glog.Infof("[old message] <%s> %v", u.Message.From, u.Message.Text)
|
glog.Infof("[old message] <%s> %v", update.Message.From, update.Message.Text)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if msg := plainFromTelegram(s.tel.Self.ID, &u); msg != nil {
|
if msg := plainFromTelegram(s.tel.Self.ID, &update); msg != nil {
|
||||||
s.telLog <- msg
|
s.telLog <- msg
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,6 +56,7 @@ func (s *server) telegramConnection(ctx context.Context) error {
|
||||||
// telegramLoop maintains a telegramConnection.
|
// telegramLoop maintains a telegramConnection.
|
||||||
func (s *server) telegramLoop(ctx context.Context) {
|
func (s *server) telegramLoop(ctx context.Context) {
|
||||||
for {
|
for {
|
||||||
|
glog.V(4).Info("telegram/debug4: Starting telegram connection loop")
|
||||||
err := s.telegramConnection(ctx)
|
err := s.telegramConnection(ctx)
|
||||||
if err == ctx.Err() {
|
if err == ctx.Err() {
|
||||||
glog.Infof("Telegram connection closing: %v", err)
|
glog.Infof("Telegram connection closing: %v", err)
|
||||||
|
|
Loading…
Reference in New Issue