Skip to content

Commit 9b49a56

Browse files
llmIIqaisjp
andauthored
Add support for ignoring IRC hosts from Discord (#82)
This should add a configuration option that allows for using nearly IRC style hostmasks to "ban" matching users in IRC from sending messages towards Discord. This would be useful for ignoring bots in channels. Tested, works, documentation and configuration example added. Co-authored-by: Qais Patankar <[email protected]>
1 parent 00353c4 commit 9b49a56

File tree

7 files changed

+88
-32
lines changed

7 files changed

+88
-32
lines changed

README.md

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -66,28 +66,29 @@ The binary takes three flags:
6666

6767
The config file is a yaml formatted file with the following fields:
6868

69-
| name | requires restart | default | optional | description |
70-
| ------------------- | ---------------- | ---------------------------------------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
71-
| `avatar_url` | No | `https://ui-avatars.com/api/?name=${USERNAME}` | Yes | The URL for the API to use to tell Discord what Avatar to use for a User when the user's avatar cannot be found at Discord already. |
72-
| `discord_token` | Yes | | No | [The bot user token](https://github.com/reactiflux/discord-irc/wiki/Creating-a-discord-bot-&-getting-a-token) |
73-
| `irc_server` | Yes | | No | IRC server address |
74-
| `channel_mappings` | No | | No | a dict with irc channel as key (prefixed with `#`) and Discord channel ID as value |
75-
| `guild_id` | No | | No | the Discord guild (server) id |
76-
| `irc_pass` | Yes | | Yes | password for connecting to the IRC server |
77-
| `suffix` | No | `~d` | Yes | appended to each Discord user's nickname when they are connected to IRC. If set to `_d2`, if the name will be `bob_d2` |
78-
| `separator` | No | `_` | Yes | used in fallback situations. If set to `-`, the **fallback name** will be like `bob-7247_d2` (where `7247` is the discord user's discriminator, and `_d2` is the suffix) |
79-
| `irc_listener_name` | Yes | `~d` | The name of the irc listener |
80-
| `puppet_username` | No | username of discord account being puppeted | Yes | username to connect to irc with |
81-
| `webirc_pass` | No | | Yes | optional, but recommended for regular (non-simple) usage. this must be obtained by the IRC sysops |
82-
| `debug` | Yes | false | Yes | debug mode |
83-
| `insecure`, | Yes | false | Yes | TLS will skip verification (but still uses TLS) |
84-
| `no_tls`, | Yes | false | Yes | turns off TLS |
85-
| `webhook_prefix`, | Yes | | No | a prefix for webhooks, so we know which ones to keep and which ones to delete |
86-
| `nickserv_identify` | No | | Yes | on connect this message will be sent: `PRIVMSG nickserv IDENTIFY <value>`, you can provide both a username and password if your ircd supports it |
87-
| `cooldown_duration` | No | 86400 (24 hours) | Yes | time in seconds for a discord user to be offline before it's puppet disconnects from irc |
88-
| `show_joinquit` | No | false | yes | displays JOIN, PART, QUIT, KICK on discord |
89-
| `max_nick_length` | No | 30 | yes | Maximum allowed nick length |
90-
| `connection_limit` | Yes | 0 | Yes | How many connections to IRC (including our listener) to spawn (limit of 0 or less means unlimited) |
69+
| name | requires restart | default | optional | description |
70+
| ----------------------- | ---------------- | ---------------------------------------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
71+
| `avatar_url` | No | `https://ui-avatars.com/api/?name=${USERNAME}` | Yes | The URL for the API to use to tell Discord what Avatar to use for a User when the user's avatar cannot be found at Discord already. |
72+
| `discord_token` | Yes | | No | [The bot user token](https://github.com/reactiflux/discord-irc/wiki/Creating-a-discord-bot-&-getting-a-token) |
73+
| `irc_server` | Yes | | No | IRC server address |
74+
| `channel_mappings` | No | | No | a dict with irc channel as key (prefixed with `#`) and Discord channel ID as value |
75+
| `guild_id` | No | | No | the Discord guild (server) id |
76+
| `irc_pass` | Yes | | Yes | password for connecting to the IRC server |
77+
| `suffix` | No | `~d` | Yes | appended to each Discord user's nickname when they are connected to IRC. If set to `_d2`, if the name will be `bob_d2` |
78+
| `separator` | No | `_` | Yes | used in fallback situations. If set to `-`, the **fallback name** will be like `bob-7247_d2` (where `7247` is the discord user's discriminator, and `_d2` is the suffix) |
79+
| `irc_listener_name` | Yes | `~d` | The name of the irc listener |
80+
| `puppet_username` | No | username of discord account being puppeted | Yes | username to connect to irc with |
81+
| `webirc_pass` | No | | Yes | optional, but recommended for regular (non-simple) usage. this must be obtained by the IRC sysops |
82+
| `debug` | Yes | false | Yes | debug mode |
83+
| `insecure`, | Yes | false | Yes | TLS will skip verification (but still uses TLS) |
84+
| `no_tls`, | Yes | false | Yes | turns off TLS |
85+
| `webhook_prefix`, | Yes | | No | a prefix for webhooks, so we know which ones to keep and which ones to delete |
86+
| `nickserv_identify` | No | | Yes | on connect this message will be sent: `PRIVMSG nickserv IDENTIFY <value>`, you can provide both a username and password if your ircd supports it |
87+
| `cooldown_duration` | No | 86400 (24 hours) | Yes | time in seconds for a discord user to be offline before it's puppet disconnects from irc |
88+
| `show_joinquit` | No | false | yes | displays JOIN, PART, QUIT, KICK on discord |
89+
| `max_nick_length` | No | 30 | yes | Maximum allowed nick length |
90+
| `ignored_irc_hostmasks` | No | | Yes | A list of IRC users identified by hostmask to not relay to Discord, uses matching syntax as in [glob](https://github.com/gobwas/glob) |
91+
| `connection_limit` | Yes | 0 | Yes | How many connections to IRC (including our listener) to spawn (limit of 0 or less means unlimited) |
9192

9293
**The filename.yaml file is continuously read from and many changes will
9394
automatically update on the bridge. This means you can add or remove channels

bridge/bridge.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"time"
99

1010
"github.com/bwmarrin/discordgo"
11+
"github.com/gobwas/glob"
1112
"github.com/pkg/errors"
1213
irc "github.com/qaisjp/go-ircevent"
1314
log "github.com/sirupsen/logrus"
@@ -17,7 +18,6 @@ import (
1718
type Config struct {
1819
AvatarURL string
1920
DiscordBotToken, GuildID string
20-
ConnectionLimit int // amount of IRC Connections we can spawn
2121

2222
// Map from Discord to IRC
2323
ChannelMappings map[string]string
@@ -28,6 +28,8 @@ type Config struct {
2828
WebIRCPass string
2929
NickServIdentify string // string: "[account] password"
3030
PuppetUsername string // Username to connect to IRC with
31+
IRCIgnores []glob.Glob
32+
ConnectionLimit int // number of IRC connections we can spawn
3133

3234
// NoTLS constrols whether to use TLS at all when connecting to the IRC server
3335
NoTLS bool

bridge/irc_connection.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,11 @@ func (i *ircConnection) experimentalNotice(nick string) {
107107
}
108108

109109
func (i *ircConnection) OnPrivateMessage(e *irc.Event) {
110+
// Ignored hostmasks
111+
if i.manager.isIgnoredHostmask(e.Source) {
112+
return
113+
}
114+
110115
// Alert private messages
111116
if string(e.Arguments[0][0]) != "#" {
112117
if e.Message() == "help" {

bridge/irc_listener.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,11 @@ func (i *ircListener) OnJoinQuitCallback(event *irc.Event) {
9595
return
9696
}
9797

98+
// Ignored hostmasks
99+
if i.bridge.ircManager.isIgnoredHostmask(event.Source) {
100+
return
101+
}
102+
98103
who := event.Nick
99104
message := event.Nick
100105
id := " (" + event.User + "@" + event.Host + ") "
@@ -215,6 +220,11 @@ func (i *ircListener) OnPrivateMessage(e *irc.Event) {
215220
return
216221
}
217222

223+
// Ignored hostmasks
224+
if i.bridge.ircManager.isIgnoredHostmask(e.Source) {
225+
return
226+
}
227+
218228
replacements := []string{}
219229
for _, con := range i.bridge.ircManager.ircConnections {
220230
replacements = append(replacements, con.nick, "<@!"+con.discord.ID+">")

bridge/irc_manager.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,15 @@ func (m *IRCManager) RequestChannels(userID string) []Mapping {
387387
return m.bridge.mappings
388388
}
389389

390+
func (m *IRCManager) isIgnoredHostmask(mask string) bool {
391+
for _, ban := range m.bridge.Config.IRCIgnores {
392+
if ban.Match(mask) {
393+
return true
394+
}
395+
}
396+
return false
397+
}
398+
390399
func (m *IRCManager) generateUsername(discordUser DiscordUser) string {
391400
if len(m.bridge.Config.PuppetUsername) > 0 {
392401
return m.bridge.Config.PuppetUsername

config.yml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,11 @@ no_tls: false
2828
debug: false
2929
webhook_prefix: "(auto-test)"
3030
# simple: true
31-
# connection_limit: 2 # Would limit this to 2 connections (a listener, and one puppet, the rest relayed)
31+
32+
# Uses matching syntax as in https://github.com/gobwas/glob
33+
# ignored_irc_hostmasks:
34+
# - "bot1!*@*"
35+
# - "*!?bot@*"
36+
37+
# This limits to 2 connections (a listener, and one puppet, the rest relayed in simple mode)
38+
# connection_limit: 2

main.go

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"time"
1212

1313
"github.com/fsnotify/fsnotify"
14+
"github.com/gobwas/glob"
1415
"github.com/pkg/errors"
1516
"github.com/qaisjp/go-discord-irc/bridge"
1617
ircnick "github.com/qaisjp/go-discord-irc/irc/nick"
@@ -61,14 +62,15 @@ func main() {
6162
log.Fatalln(errors.Wrap(err, "could not read config"))
6263
}
6364

64-
discordBotToken := viper.GetString("discord_token") // Discord Bot User Token
65-
channelMappings := viper.GetStringMapString("channel_mappings") // Discord:IRC mappings in format '#discord1:#irc1,#discord2:#irc2,...'
66-
ircServer := viper.GetString("irc_server") // Server address to use, example `irc.freenode.net:7000`.
67-
ircPassword := viper.GetString("irc_pass") // Optional password for connecting to the IRC server
68-
guildID := viper.GetString("guild_id") // Guild to use
69-
webIRCPass := viper.GetString("webirc_pass") // Password for WEBIRC
70-
identify := viper.GetString("nickserv_identify") // NickServ IDENTIFY for Listener
71-
connectionLimit := viper.GetInt("connection_limit") // Limiter on how many IRC Connections we can spawn
65+
discordBotToken := viper.GetString("discord_token") // Discord Bot User Token
66+
channelMappings := viper.GetStringMapString("channel_mappings") // Discord:IRC mappings in format '#discord1:#irc1,#discord2:#irc2,...'
67+
ircServer := viper.GetString("irc_server") // Server address to use, example `irc.freenode.net:7000`.
68+
ircPassword := viper.GetString("irc_pass") // Optional password for connecting to the IRC server
69+
guildID := viper.GetString("guild_id") // Guild to use
70+
webIRCPass := viper.GetString("webirc_pass") // Password for WEBIRC
71+
identify := viper.GetString("nickserv_identify") // NickServ IDENTIFY for Listener
72+
ircIgnores := viper.GetStringSlice("ignored_irc_hostmasks") // IRC hosts to not relay to Discord
73+
connectionLimit := viper.GetInt("connection_limit") // Limiter on how many IRC Connections we can spawn
7274
//
7375
if !*debugMode {
7476
*debugMode = viper.GetBool("debug")
@@ -116,6 +118,7 @@ func main() {
116118
log.Warnln("Channel mappings are missing!")
117119
}
118120

121+
matchers := setupHostmaskMatchers(ircIgnores)
119122
SetLogDebug(*debugMode)
120123

121124
dib, err := bridge.New(&bridge.Config{
@@ -126,6 +129,7 @@ func main() {
126129
IRCServer: ircServer,
127130
IRCServerPass: ircPassword,
128131
ConnectionLimit: connectionLimit,
132+
IRCIgnores: matchers,
129133
PuppetUsername: puppetUsername,
130134
NickServIdentify: identify,
131135
WebIRCPass: webIRCPass,
@@ -176,6 +180,9 @@ func main() {
176180
dib.SetIRCListenerName(ircUsername)
177181
}
178182

183+
ircIgnores := viper.GetStringSlice("ignored_irc_hostmasks")
184+
dib.Config.IRCIgnores = setupHostmaskMatchers(ircIgnores)
185+
179186
avatarURL := viper.GetString("avatar_url")
180187
dib.Config.AvatarURL = avatarURL
181188

@@ -211,6 +218,21 @@ func main() {
211218
dib.Close()
212219
}
213220

221+
func setupHostmaskMatchers(hostmasks []string) []glob.Glob {
222+
var matchers []glob.Glob
223+
for _, mask := range hostmasks {
224+
g, err := glob.Compile(mask)
225+
if err != nil {
226+
log.WithField("error", err).WithField("hostmask", mask).Errorln("Failed to compile hostmask ban!")
227+
continue
228+
}
229+
230+
matchers = append(matchers, g)
231+
}
232+
233+
return matchers
234+
}
235+
214236
func SetLogDebug(debug bool) {
215237
logger := log.StandardLogger()
216238
if debug {

0 commit comments

Comments
 (0)