Skip to content

Commit 30633fc

Browse files
llmIIqaisjp
andcommitted
Initial support for message formatting
Allows for the user to define how messages are formatted back and forth between Discord and IRC. Co-authored-by: Qais Patankar <[email protected]>
1 parent df78db0 commit 30633fc

File tree

5 files changed

+114
-28
lines changed

5 files changed

+114
-28
lines changed

bridge/bridge.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ type Config struct {
3737
IRCPuppetPrejoinCommands []string
3838
IRCListenerPrejoinCommands []string
3939

40+
DiscordFormat map[string]string // formatting for non-PRIVMSG relays from IRC to Discord
41+
IRCFormat string // format for messages relayed in simple mode
42+
4043
// NoTLS constrols whether to use TLS at all when connecting to the IRC server
4144
NoTLS bool
4245

bridge/irc_connection.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,12 +143,15 @@ func (i *ircConnection) OnPrivateMessage(e *irc.Event) {
143143
}
144144

145145
d := i.manager.bridge.discord
146+
msg := i.manager.formatDiscordMessage("PM", e, e.Message(), "")
147+
148+
// if we have an empty message
149+
if msg == "" {
150+
return // do nothing, Discord doesn't like those
151+
}
146152

147153
i.introducePM(e.Nick)
148154

149-
msg := fmt.Sprintf(
150-
"%s,%s - %s@%s: %s", e.Connection.Server, e.Source,
151-
e.Nick, i.manager.bridge.Config.Discriminator, e.Message())
152155
_, err := d.ChannelMessageSend(i.pmDiscordChannel, msg)
153156
if err != nil {
154157
log.Warnln("Could not send PM", i.discord, err)

bridge/irc_listener.go

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package bridge
22

33
import (
4-
"fmt"
54
"strings"
65

76
ircf "github.com/qaisjp/go-discord-irc/irc/format"
@@ -67,12 +66,17 @@ func (i *ircListener) OnNickRelayToDiscord(event *irc.Event) {
6766
return
6867
}
6968

70-
oldNick := event.Nick
7169
newNick := event.Message()
70+
message := i.bridge.ircManager.formatDiscordMessage(event.Code, event, newNick, "")
71+
72+
// if the message is empty...
73+
if message == "" {
74+
return // do nothing, Discord doesn't like empty messages anyway
75+
}
7276

7377
msg := IRCMessage{
7478
Username: "",
75-
Message: fmt.Sprintf("_%s changed their nick to %s_", oldNick, newNick),
79+
Message: message,
7680
}
7781

7882
for _, m := range i.bridge.mappings {
@@ -136,29 +140,33 @@ func (i *ircListener) OnJoinQuitCallback(event *irc.Event) {
136140
return
137141
}
138142

139-
who := event.Nick
140-
message := event.Nick
141-
id := " (" + event.User + "@" + event.Host + ") "
143+
message := ""
144+
content := ""
145+
target := ""
146+
manager := i.bridge.ircManager
142147

143148
switch event.Code {
144149
case "STJOIN":
145-
message += " joined" + id
150+
message = manager.formatDiscordMessage(event.Code, event, "", "")
146151
case "STPART":
147-
message += " left" + id
148152
if len(event.Arguments) > 1 {
149-
message += ": " + event.Arguments[1]
153+
content = event.Arguments[1]
150154
}
155+
message = manager.formatDiscordMessage(event.Code, event, content, "")
151156
case "STQUIT":
152-
message += " quit" + id
153-
154-
reason := event.Nick
157+
content := event.Nick
155158
if len(event.Arguments) == 1 {
156-
reason = event.Arguments[0]
159+
content = event.Arguments[0]
157160
}
158-
message += "Quit: " + reason
161+
message = manager.formatDiscordMessage(event.Code, event, content, "")
159162
case "KICK":
160-
who = event.Arguments[1]
161-
message = event.Arguments[1] + " was kicked by " + event.Nick + ": " + event.Arguments[2]
163+
target, content = event.Arguments[1], event.Arguments[2]
164+
message = manager.formatDiscordMessage(event.Code, event, content, target)
165+
}
166+
167+
// if the message is empty...
168+
if message == "" {
169+
return // do nothing, Discord doesn't like empty messages anyway
162170
}
163171

164172
msg := IRCMessage{
@@ -173,10 +181,10 @@ func (i *ircListener) OnJoinQuitCallback(event *irc.Event) {
173181
channel := m.IRCChannel
174182
channelObj, ok := i.Connection.GetChannel(channel)
175183
if !ok {
176-
log.WithField("channel", channel).WithField("who", who).Warnln("Trying to process QUIT. Channel not found in irc listener cache.")
184+
log.WithField("channel", channel).WithField("who", event.Nick).Warnln("Trying to process QUIT. Channel not found in irc listener cache.")
177185
continue
178186
}
179-
if _, ok := channelObj.GetUser(who); !ok {
187+
if _, ok := channelObj.GetUser(event.Nick); !ok {
180188
continue
181189
}
182190
msg.IRCChannel = channel

bridge/irc_manager.go

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,18 @@ func (m *IRCManager) generateNickname(discord DiscordUser) string {
368368
return newNick
369369
}
370370

371+
func (m *IRCManager) formatIRCMessage(message *DiscordMessage, content string) string {
372+
msg := m.bridge.Config.IRCFormat
373+
length := len(message.Author.Username)
374+
msg = strings.ReplaceAll(msg, "${USER}", message.Author.Username[:1]+"\u200B"+message.Author.Username[1:length])
375+
msg = strings.ReplaceAll(msg, "${DISCRIMINATOR}", message.Author.Discriminator)
376+
msg = strings.ReplaceAll(msg, "${CONTENT}", content)
377+
378+
// we don't do trimming and later checks here, IRC doesn't mind blank messages or at least doesn't complain
379+
// as loudly as Discord
380+
return msg
381+
}
382+
371383
// SendMessage sends a broken down Discord Message to a particular IRC channel.
372384
func (m *IRCManager) SendMessage(channel string, msg *DiscordMessage) {
373385
if m.ircIgnoredDiscord(msg.Author.ID) {
@@ -382,14 +394,8 @@ func (m *IRCManager) SendMessage(channel string, msg *DiscordMessage) {
382394

383395
// Person is appearing offline (or the bridge is running in Simple Mode)
384396
if !ok {
385-
length := len(msg.Author.Username)
386397
for _, line := range strings.Split(content, "\n") {
387-
m.bridge.ircListener.Privmsg(channel, fmt.Sprintf(
388-
"<%s#%s> %s",
389-
msg.Author.Username[:1]+"\u200B"+msg.Author.Username[1:length],
390-
msg.Author.Discriminator,
391-
line,
392-
))
398+
m.bridge.ircListener.Privmsg(channel, m.formatIRCMessage(msg, line))
393399
}
394400
return
395401
}
@@ -423,6 +429,22 @@ func (m *IRCManager) SendMessage(channel string, msg *DiscordMessage) {
423429
}
424430
}
425431

432+
func (m *IRCManager) formatDiscordMessage(msgFormat string, e *irc.Event, content string, target string) string {
433+
msg := ""
434+
if format, ok := m.bridge.Config.DiscordFormat[strings.ToLower(msgFormat)]; ok && format != "" {
435+
msg = format
436+
msg = strings.ReplaceAll(msg, "${NICK}", e.Nick)
437+
msg = strings.ReplaceAll(msg, "${IDENT}", e.User)
438+
msg = strings.ReplaceAll(msg, "${HOST}", e.Host)
439+
msg = strings.ReplaceAll(msg, "${CONTENT}", content)
440+
msg = strings.ReplaceAll(msg, "${TARGET}", target)
441+
msg = strings.ReplaceAll(msg, "${SERVER}", e.Connection.Server)
442+
msg = strings.ReplaceAll(msg, "${DISCRIMINATOR}", m.bridge.Config.Discriminator)
443+
}
444+
445+
return strings.Trim(msg, " ")
446+
}
447+
426448
// RequestChannels finds all the Discord channels this user belongs to,
427449
// and then find pairings in the global pairings list
428450
// Currently just returns all participating IRC channels

main.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package main
22

33
import (
44
"flag"
5+
"fmt"
56
"os"
67
"os/signal"
78
"path/filepath"
@@ -92,10 +93,21 @@ func main() {
9293
//
9394
viper.SetDefault("irc_puppet_prejoin_commands", []string{"MODE ${NICK} +D"})
9495
ircPuppetPrejoinCommands := viper.GetStringSlice("irc_puppet_prejoin_commands") // Commands for each connection to send before joining channels
96+
rawDiscordFormat := viper.GetStringMapString("discord_format")
97+
var discordFormat map[string]string
98+
if df, err := setupDiscordFormat(rawDiscordFormat); err == nil {
99+
discordFormat = df
100+
} else {
101+
log.WithError(err).Fatal("discord_format setting is invalid")
102+
return
103+
}
95104
//
96105
viper.SetDefault("avatar_url", "https://ui-avatars.com/api/?name=${USERNAME}")
97106
avatarURL := viper.GetString("avatar_url")
98107
//
108+
viper.SetDefault("irc_format", "<${USER}#${DISCRIMINATOR}> ${CONTENT}")
109+
ircFormat := viper.GetString("irc_format")
110+
//
99111
viper.SetDefault("irc_listener_name", "~d")
100112
ircUsername := viper.GetString("irc_listener_name") // Name for IRC-side bot, for listening to messages.
101113
// Name to Connect to IRC puppet account with
@@ -140,7 +152,9 @@ func main() {
140152
AvatarURL: avatarURL,
141153
Discriminator: discriminator,
142154
DiscordBotToken: discordBotToken,
155+
DiscordFormat: discordFormat,
143156
GuildID: guildID,
157+
IRCFormat: ircFormat,
144158
IRCListenerName: ircUsername,
145159
IRCServer: ircServer,
146160
IRCServerPass: ircPassword,
@@ -219,6 +233,14 @@ func main() {
219233
}
220234
dib.Config.DiscordIgnores = discordIgnores
221235

236+
rawDiscordFormat := viper.GetStringMapString("discord_format")
237+
if discordFormat, err := setupDiscordFormat(rawDiscordFormat); err == nil {
238+
dib.Config.DiscordFormat = discordFormat
239+
} else {
240+
log.WithError(err).Error("discord_format setting is invalid, this setting has not been updated")
241+
}
242+
dib.Config.IRCFormat = viper.GetString("irc_format")
243+
222244
chans := viper.GetStringMapString("channel_mappings")
223245
equalChans := reflect.DeepEqual(chans, channelMappings)
224246
if !equalChans {
@@ -259,6 +281,34 @@ func setupHostmaskMatchers(hostmasks []string) []glob.Glob {
259281
return matchers
260282
}
261283

284+
func setupDiscordFormat(discordFormat map[string]string) (map[string]string, error) {
285+
var err error
286+
// lowercase to match that YAML lowercases it
287+
discordFormatDefaults := map[string]string{
288+
"pm": "${SERVER},${NICK}!${IDENT}@${HOST} - ${NICK}@${DISCRIMINATOR}: ${CONTENT}",
289+
"join": "_${NICK} joined (${IDENT}@${HOST})_",
290+
"part": "_${NICK} left (${IDENT}@${HOST}) - ${CONTENT}_",
291+
"quit": "_${NICK} quit (${IDENT}@${HOST}) - Quit: ${CONTENT}_",
292+
"kick": "_${TARGET} was kicked by ${NICK} - ${CONTENT}_",
293+
"nick": "_${NICK} changed nick to ${CONTENT}_",
294+
}
295+
296+
for ev, format := range discordFormatDefaults {
297+
if df, ok := discordFormat[ev]; !ok || df == "" {
298+
discordFormat[ev] = format
299+
}
300+
}
301+
302+
for ev := range discordFormat {
303+
if _, ok := discordFormatDefaults[ev]; !ok {
304+
err = fmt.Errorf("Unknown format key %s", ev)
305+
break
306+
}
307+
}
308+
309+
return discordFormat, err
310+
}
311+
262312
func SetLogDebug(debug bool) {
263313
logger := log.StandardLogger()
264314
if debug {

0 commit comments

Comments
 (0)