From 67ff1ca58de4e5b6d0059f36cfd5720bafd15f98 Mon Sep 17 00:00:00 2001 From: Tim Zabel Date: Sat, 16 May 2020 20:06:28 -0400 Subject: [PATCH 1/3] Add zero width space between Telegram usernames to prevent pinging across platforms. ResolveUserName is now used instead of user.String() for retrieving username information. For additional details, see: https://github.com/42wim/matterbridge/issues/175. --- internal/handlers/telegram/handler.go | 14 ++++++++------ internal/handlers/telegram/handler_test.go | 2 +- internal/handlers/telegram/helpers.go | 19 ++++++++++++++++--- internal/handlers/telegram/helpers_test.go | 15 ++++++++++++--- 4 files changed, 37 insertions(+), 13 deletions(-) diff --git a/internal/handlers/telegram/handler.go b/internal/handlers/telegram/handler.go index 270f52c9..da964a2f 100644 --- a/internal/handlers/telegram/handler.go +++ b/internal/handlers/telegram/handler.go @@ -54,9 +54,10 @@ messageHandler handles the Message Telegram Object, which formats the Telegram update into a simple string for IRC. */ func messageHandler(tg *Client, u tgbotapi.Update) { + username := ResolveUserName(u.Message.From) formatted := fmt.Sprintf("%s%s%s %s", tg.Settings.Prefix, - u.Message.From.String(), + username, tg.Settings.Suffix, // Trim unexpected trailing whitespace strings.Trim(u.Message.Text, " ")) @@ -69,7 +70,7 @@ joinHandler handles when users join the Telegram group func joinHandler(tg *Client, users *[]tgbotapi.User) { if tg.IRCSettings.ShowJoinMessage { for _, user := range *users { - username := GetUsername(&user) + username := GetFullUsername(&user) formatted := username + " has joined the Telegram Group!" tg.sendToIrc(formatted) } @@ -81,7 +82,7 @@ partHandler handles when users leave the Telegram group */ func partHandler(tg *Client, user *tgbotapi.User) { if tg.IRCSettings.ShowLeaveMessage { - username := GetUsername(user) + username := GetFullUsername(user) formatted := username + " has left the Telegram Group!" tg.sendToIrc(formatted) @@ -108,8 +109,8 @@ photoHandler handles the Message.Photo Telegram object. Only acknowledges Photo exists, and sends notification to IRC */ func photoHandler(tg *Client, u tgbotapi.Update) { - user := u.Message.From - formatted := user.String() + " shared a photo on Telegram with caption: '" + + username := ResolveUserName(u.Message.From) + formatted := username + " shared a photo on Telegram with caption: '" + u.Message.Caption + "'" tg.sendToIrc(formatted) @@ -120,7 +121,8 @@ documentHandler receives a document object from Telegram, and sends a notification to IRC. */ func documentHandler(tg *Client, u *tgbotapi.Message) { - formatted := u.From.String() + " shared a file" + username := ResolveUserName(u.From) + formatted := username + " shared a file" if u.Document.MimeType != "" { formatted += " (" + u.Document.MimeType + ")" } diff --git a/internal/handlers/telegram/handler_test.go b/internal/handlers/telegram/handler_test.go index 14a50e0b..cc31e0f2 100644 --- a/internal/handlers/telegram/handler_test.go +++ b/internal/handlers/telegram/handler_test.go @@ -505,7 +505,7 @@ func TestMessageRandomWithoutUsername(t *testing.T) { FirstName: "testing", LastName: "123", } - correct := fmt.Sprintf("<%s> Random Text", testUser.String()) + correct := fmt.Sprintf("<%s> Random Text", testUser.FirstName) updateObj := tgbotapi.Update{ Message: &tgbotapi.Message{ diff --git a/internal/handlers/telegram/helpers.go b/internal/handlers/telegram/helpers.go index e954d744..bf6170a5 100644 --- a/internal/handlers/telegram/helpers.go +++ b/internal/handlers/telegram/helpers.go @@ -5,12 +5,25 @@ import ( ) /* -GetUsername returns the name and username of a user. Since usernames are optional +ResolveUserName does basic cleanup if a user does not have a username on Telegram. +*/ +func ResolveUserName(u *tgbotapi.User) string { + if u.UserName == "" { + return u.FirstName + } + // Add ZWSP to prevent pinging across platforms + // See https://github.com/42wim/matterbridge/issues/175 + username := u.UserName[:1] + "" + u.UserName[1:] + return username +} + +/* +GetFullUsername returns the name and username of a user. Since usernames are optional on Telegram, we first need to check to see if they have one set. */ -func GetUsername(u *tgbotapi.User) string { +func GetFullUsername(u *tgbotapi.User) string { if u.UserName == "" { return u.FirstName } - return u.FirstName + " (@" + u.UserName + ")" + return u.FirstName + " (@" + u.UserName[:1] + "" + u.UserName[1:] + ")" } diff --git a/internal/handlers/telegram/helpers_test.go b/internal/handlers/telegram/helpers_test.go index 4e554aea..6018be52 100644 --- a/internal/handlers/telegram/helpers_test.go +++ b/internal/handlers/telegram/helpers_test.go @@ -8,8 +8,9 @@ import ( func TestGetUsername(t *testing.T) { username := &tgbotapi.User{ID: 1, FirstName: "John", UserName: "jsmith"} - correct := username.FirstName + " (@" + username.UserName + ")" - name := GetUsername(username) + correct := username.FirstName + " (@" + username.UserName[:1] + + "" + username.UserName[1:] + ")" + name := GetFullUsername(username) assert.Equal(t, correct, name) } @@ -17,7 +18,15 @@ func TestGetUsername(t *testing.T) { func TestGetNoUsername(t *testing.T) { username := &tgbotapi.User{ID: 1, FirstName: "John"} correct := username.FirstName - name := GetUsername(username) + name := GetFullUsername(username) + + assert.Equal(t, correct, name) +} + +func TestResolveUserName(t *testing.T) { + username := &tgbotapi.User{ID: 1, FirstName: "John", UserName: "jsmith"} + correct := username.UserName[:1] + "" + username.UserName[1:] + name := ResolveUserName(username) assert.Equal(t, correct, name) } From 48bc3b7d017570d576946d1681cde373fb127db2 Mon Sep 17 00:00:00 2001 From: Tim Zabel Date: Sat, 23 May 2020 16:45:05 -0400 Subject: [PATCH 2/3] Add config check for ZWSP. Helper methods now better reflect their actions. --- internal/config.go | 1 + internal/handlers/telegram/handler.go | 11 +++++-- internal/handlers/telegram/handler_test.go | 37 ++++++++++++++++++++++ internal/handlers/telegram/helpers.go | 21 +++++++++--- internal/handlers/telegram/helpers_test.go | 32 ++++++++++++------- 5 files changed, 83 insertions(+), 19 deletions(-) diff --git a/internal/config.go b/internal/config.go index fec11cdf..a093abf7 100644 --- a/internal/config.go +++ b/internal/config.go @@ -28,6 +28,7 @@ type IRCSettings struct { Suffix string `env:"IRC_SUFFIX" envDefault:">"` ShowJoinMessage bool `env:"IRC_SHOW_JOIN_MESSAGE" envDefault:"true"` ShowLeaveMessage bool `env:"IRC_SHOW_LEAVE_MESSAGE" envDefault:"true"` + ShowZWSP bool `env:"IRC_SHOW_ZWSP" envDefault:"true"` NickServPassword string `env:"IRC_NICKSERV_PASS" envDefault:""` NickServService string `env:"IRC_NICKSERV_SERVICE" envDefault:""` EditedPrefix string `env:"IRC_EDITED_PREFIX" envDefault:"[EDIT] "` diff --git a/internal/handlers/telegram/handler.go b/internal/handlers/telegram/handler.go index da964a2f..b0c67773 100644 --- a/internal/handlers/telegram/handler.go +++ b/internal/handlers/telegram/handler.go @@ -54,7 +54,12 @@ messageHandler handles the Message Telegram Object, which formats the Telegram update into a simple string for IRC. */ func messageHandler(tg *Client, u tgbotapi.Update) { - username := ResolveUserName(u.Message.From) + username := "" + if tg.IRCSettings.ShowZWSP { + username = ZwspUsername(u.Message.From) + } else { + username = GetUsername(u.Message.From) + } formatted := fmt.Sprintf("%s%s%s %s", tg.Settings.Prefix, username, @@ -109,7 +114,7 @@ photoHandler handles the Message.Photo Telegram object. Only acknowledges Photo exists, and sends notification to IRC */ func photoHandler(tg *Client, u tgbotapi.Update) { - username := ResolveUserName(u.Message.From) + username := GetUsername(u.Message.From) formatted := username + " shared a photo on Telegram with caption: '" + u.Message.Caption + "'" @@ -121,7 +126,7 @@ documentHandler receives a document object from Telegram, and sends a notification to IRC. */ func documentHandler(tg *Client, u *tgbotapi.Message) { - username := ResolveUserName(u.From) + username := GetUsername(u.From) formatted := username + " shared a file" if u.Document.MimeType != "" { formatted += " (" + u.Document.MimeType + ")" diff --git a/internal/handlers/telegram/handler_test.go b/internal/handlers/telegram/handler_test.go index cc31e0f2..df6f8681 100644 --- a/internal/handlers/telegram/handler_test.go +++ b/internal/handlers/telegram/handler_test.go @@ -490,6 +490,9 @@ func TestMessageRandomWithUsername(t *testing.T) { Prefix: "<", Suffix: ">", }, + IRCSettings: &internal.IRCSettings{ + ShowZWSP: false, + }, sendToIrc: func(s string) { assert.Equal(t, correct, s) }, @@ -518,6 +521,40 @@ func TestMessageRandomWithoutUsername(t *testing.T) { Prefix: "<", Suffix: ">", }, + IRCSettings: &internal.IRCSettings{ + ShowZWSP: false, + }, + sendToIrc: func(s string) { + assert.Equal(t, correct, s) + }, + } + + messageHandler(clientObj, updateObj) +} + +func TestMessageZwsp(t *testing.T) { + testUser := &tgbotapi.User{ + ID: 1, + UserName: "test", + FirstName: "testing", + LastName: "123", + } + correct := fmt.Sprintf("<%s> Random Text", "t"+""+"est") + + updateObj := tgbotapi.Update{ + Message: &tgbotapi.Message{ + From: testUser, + Text: "Random Text", + }, + } + clientObj := &Client{ + Settings: &internal.TelegramSettings{ + Prefix: "<", + Suffix: ">", + }, + IRCSettings: &internal.IRCSettings{ + ShowZWSP: true, + }, sendToIrc: func(s string) { assert.Equal(t, correct, s) }, diff --git a/internal/handlers/telegram/helpers.go b/internal/handlers/telegram/helpers.go index bf6170a5..96200683 100644 --- a/internal/handlers/telegram/helpers.go +++ b/internal/handlers/telegram/helpers.go @@ -5,9 +5,9 @@ import ( ) /* -ResolveUserName does basic cleanup if a user does not have a username on Telegram. +GetUsername returns a Telegram user's username if one is set, or first name otherwise. */ -func ResolveUserName(u *tgbotapi.User) string { +func GetUsername(u *tgbotapi.User) string { if u.UserName == "" { return u.FirstName } @@ -18,8 +18,7 @@ func ResolveUserName(u *tgbotapi.User) string { } /* -GetFullUsername returns the name and username of a user. Since usernames are optional -on Telegram, we first need to check to see if they have one set. +GetFullUsername returns both the Telegram user's first name and username, if available. */ func GetFullUsername(u *tgbotapi.User) string { if u.UserName == "" { @@ -27,3 +26,17 @@ func GetFullUsername(u *tgbotapi.User) string { } return u.FirstName + " (@" + u.UserName[:1] + "" + u.UserName[1:] + ")" } + +/* +ZwspUsername adds a zero-width space after the first character of a Telegram user's +username. +*/ +func ZwspUsername(u *tgbotapi.User) string { + if u.UserName == "" { + return u.FirstName + } + // Add ZWSP to prevent pinging across platforms + // See https://github.com/42wim/matterbridge/issues/175 + username := u.UserName[:1] + "" + u.UserName[1:] + return username +} diff --git a/internal/handlers/telegram/helpers_test.go b/internal/handlers/telegram/helpers_test.go index 6018be52..24d350b0 100644 --- a/internal/handlers/telegram/helpers_test.go +++ b/internal/handlers/telegram/helpers_test.go @@ -6,27 +6,35 @@ import ( "testing" ) -func TestGetUsername(t *testing.T) { - username := &tgbotapi.User{ID: 1, FirstName: "John", UserName: "jsmith"} - correct := username.FirstName + " (@" + username.UserName[:1] + - "" + username.UserName[1:] + ")" - name := GetFullUsername(username) +func TestGetFullUsername(t *testing.T) { + user := &tgbotapi.User{ID: 1, FirstName: "John", UserName: "jsmith"} + correct := user.FirstName + " (@" + user.UserName[:1] + + "" + user.UserName[1:] + ")" + name := GetFullUsername(user) assert.Equal(t, correct, name) } func TestGetNoUsername(t *testing.T) { - username := &tgbotapi.User{ID: 1, FirstName: "John"} - correct := username.FirstName - name := GetFullUsername(username) + user := &tgbotapi.User{ID: 1, FirstName: "John"} + correct := user.FirstName + name := GetFullUsername(user) + + assert.Equal(t, correct, name) +} + +func TestGetUsername(t *testing.T) { + user := &tgbotapi.User{ID: 1, FirstName: "John", UserName: "jsmith"} + correct := user.UserName[:1] + "" + user.UserName[1:] + name := GetUsername(user) assert.Equal(t, correct, name) } -func TestResolveUserName(t *testing.T) { - username := &tgbotapi.User{ID: 1, FirstName: "John", UserName: "jsmith"} - correct := username.UserName[:1] + "" + username.UserName[1:] - name := ResolveUserName(username) +func TestZwspUsername(t *testing.T) { + user := &tgbotapi.User{ID: 1, FirstName: "John", UserName: "jsmith"} + correct := "j" + "" + "smith" + name := ZwspUsername(user) assert.Equal(t, correct, name) } From 514869df4edff83b68012ec5dab23129a1478f3a Mon Sep 17 00:00:00 2001 From: Tim Zabel Date: Sat, 23 May 2020 18:19:34 -0400 Subject: [PATCH 3/3] Make return values more concise. --- internal/handlers/telegram/helpers.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/internal/handlers/telegram/helpers.go b/internal/handlers/telegram/helpers.go index 96200683..4783dbc2 100644 --- a/internal/handlers/telegram/helpers.go +++ b/internal/handlers/telegram/helpers.go @@ -13,8 +13,7 @@ func GetUsername(u *tgbotapi.User) string { } // Add ZWSP to prevent pinging across platforms // See https://github.com/42wim/matterbridge/issues/175 - username := u.UserName[:1] + "" + u.UserName[1:] - return username + return u.UserName[:1] + "" + u.UserName[1:] } /* @@ -37,6 +36,5 @@ func ZwspUsername(u *tgbotapi.User) string { } // Add ZWSP to prevent pinging across platforms // See https://github.com/42wim/matterbridge/issues/175 - username := u.UserName[:1] + "" + u.UserName[1:] - return username + return u.UserName[:1] + "" + u.UserName[1:] }