diff --git a/.circleci/config.yml b/.circleci/config.yml index d22e92b..1dadb53 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -9,9 +9,10 @@ version: 2.1 workflows: main: jobs: - - go_1-15 - - go_1-16 - - go_1-17 + - go_1-18 + - go_1-19 + - go_1-20 + - go_1-25 - build_docs commands: @@ -19,7 +20,7 @@ commands: description: Run linter checks on TeleIRC. steps: - checkout - - run: + - run: name: Download and install golintci-lint. command: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sudo sh -s -- -b $(go env GOPATH)/bin v1.43.0 - run: @@ -34,21 +35,21 @@ commands: command: go test -coverprofile=c.out ./... jobs: - go_1-15: + go_1-18: docker: - - image: cimg/go:1.15 + - image: cimg/go:1.18 steps: - golintci-lint - teleirc-test - go_1-16: + go_1-19: docker: - - image: cimg/go:1.16 + - image: cimg/go:1.19 steps: - golintci-lint - teleirc-test - go_1-17: + go_1-20: docker: - - image: cimg/go:1.17 + - image: cimg/go:1.20 steps: - golintci-lint - run: @@ -64,6 +65,30 @@ jobs: command: | sed -i 's/github.com\/ritlug\/teleirc\///g' c.out /tmp/cc-test-reporter after-build + go_1-25: + docker: + - image: cimg/go:1.25 + steps: + - checkout + - run: + name: Download and install golintci-lint. + command: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sudo sh -s -- -b $(go env GOPATH)/bin v2.6.1 + - run: + name: Run Go linter checks. + command: golangci-lint run + - teleirc-test + - run: + name: Display test coverage summary. + command: | + go tool cover -func=c.out + echo "Total coverage:" + go tool cover -func=c.out | grep total | awk '{print $3}' + - run: + name: Generate HTML coverage report. + command: go tool cover -html=c.out -o coverage.html + - store_artifacts: + path: coverage.html + destination: coverage-report build_docs: docker: - image: cimg/python:3.10 diff --git a/.github/workflows/publish_docker_image.yml b/.github/workflows/publish_docker_image.yml index 822d48f..bec890c 100644 --- a/.github/workflows/publish_docker_image.yml +++ b/.github/workflows/publish_docker_image.yml @@ -1,49 +1,47 @@ -name: Build and Push Docker Image - +name: Build and Publish Docker Image + +#on: +# push: +# branches: +# - main +# tags: +# - 'v*' on: + pull_request: + branches: + - main push: - branches: [main] - -env: - REGISTRY: ghcr.io - IMAGE_NAME: ${{ github.repository }} - -concurrency: - group: teleirc-docker-${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true + tags: + - 'v*' jobs: - build-and-push-image: - name: Build and Push Image - + build-and-push: + name: Build and Push Docker Image to GHCR runs-on: ubuntu-latest permissions: contents: read packages: write + id-token: write steps: - - name: Checkout repository + - name: Checkout code uses: actions/checkout@v4 - - name: Log in to the Container registry - uses: docker/login-action@v2 + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 with: - registry: ${{ env.REGISTRY }} + registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Extract metadata (tags, labels) for Docker - id: meta - uses: docker/metadata-action@v4 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - github-token: ${{ secrets.GITHUB_TOKEN }} + - name: Set up docker buildx + uses: docker/setup-buildx-action@v3 - - name: Build and push Docker image - uses: docker/build-push-action@v4 + - name: Build and push docker image + uses: docker/build-push-action@v6 with: - push: true context: . file: ./deployments/container/Dockerfile - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} + push: true + tags: | + ghcr.io/ritlug/${{ github.repository##*/ }}:${{ github.sha }} diff --git a/cmd/teleirc.go b/cmd/teleirc.go index fa8921c..1217ba6 100644 --- a/cmd/teleirc.go +++ b/cmd/teleirc.go @@ -6,6 +6,7 @@ import ( "os" "os/signal" "syscall" + "strconv" tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api" "github.com/ritlug/teleirc/internal" @@ -15,7 +16,9 @@ import ( var ( flagPath = flag.String("conf", ".env", "config file") - flagDebug = flag.Bool("debug", false, "enable debugging output") + flagDebug = flag.Bool("debug", func() bool { env, _ := strconv.ParseBool(os.Getenv("DEBUG")); return env }(), "enable debugging output") + flagMuteIrc = flag.Bool("muteirc", func() bool { env, _ := strconv.ParseBool(os.Getenv("DISABLE_RELAY_TO_IRC")); return env }(), "disable Telegram messages to IRC") + flagMuteTg = flag.Bool("mutetelegram", func() bool { env, _ := strconv.ParseBool(os.Getenv("DISABLE_RELAY_TO_TELEGRAM")); return env }(), "disable IRC messages to Telegram") flagVersion = flag.Bool("version", false, "displays current version of TeleIRC") version string ) @@ -33,6 +36,13 @@ func main() { // Notify that logger is enabled logger.LogDebug("Debug mode enabled!") + if *flagMuteIrc { + logger.LogInfo("Relaying messages to IRC is turned OFF!") + } + if *flagMuteTg { + logger.LogInfo("Relaying messages to Telegram is turned OFF!") + } + settings, err := internal.LoadConfig(*flagPath) if err != nil { logger.LogError(err) @@ -43,10 +53,10 @@ func main() { signal.Notify(signalChannel, os.Interrupt, syscall.SIGTERM) var tgapi *tgbotapi.BotAPI - tgClient := tg.NewClient(&settings.Telegram, &settings.IRC, &settings.Imgur, tgapi, logger) + tgClient := tg.NewClient(&settings.Telegram, &settings.IRC, &settings.Imgur, tgapi, logger, *flagMuteIrc) tgChan := make(chan error) - ircClient := irc.NewClient(&settings.IRC, &settings.Telegram, logger) + ircClient := irc.NewClient(&settings.IRC, &settings.Telegram, logger, *flagMuteTg) ircChan := make(chan error) go ircClient.StartBot(ircChan, tgClient.SendMessage) diff --git a/deployments/container/Dockerfile b/deployments/container/Dockerfile index 633cd8a..d813999 100644 --- a/deployments/container/Dockerfile +++ b/deployments/container/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.22-alpine AS builder +FROM golang:1.25-alpine AS builder WORKDIR /app @@ -16,8 +16,6 @@ RUN adduser -D teleirc-user USER teleirc-user COPY --from=builder /app/teleirc /opt/teleirc/teleirc -COPY --from=builder /app/.env /opt/teleirc/conf WORKDIR /opt/teleirc ENTRYPOINT [ "./teleirc" ] -CMD [ "-conf", "/opt/teleirc/conf", "-debug", "true" ] diff --git a/deployments/container/docker-compose.yml.example b/deployments/container/docker-compose.yml.example index 59ce7db..63797c1 100644 --- a/deployments/container/docker-compose.yml.example +++ b/deployments/container/docker-compose.yml.example @@ -6,7 +6,6 @@ version: '3' services: teleirc: build: - context: ../../ - dockerfile: ./deployments/container/Dockerfile - env_file: ../../.env + context: https://github.com/RITlug/teleirc.git + dockerfile: deployments/container/Dockerfile user: teleirc diff --git a/docs/user/config-file-glossary.rst b/docs/user/config-file-glossary.rst index 21beffc..309fbef 100644 --- a/docs/user/config-file-glossary.rst +++ b/docs/user/config-file-glossary.rst @@ -3,8 +3,27 @@ Config file glossary #################### This page is a glossary of different settings in the ``env.example`` configuration file. -All values shown are the default settings. -This glossary is intended for advanced users. + +.. note:: + All values shown are the default settings. + This glossary is intended for advanced users. + + +************ +General settings +************ + +Configuration settings +======================== + +``DEBUG=false`` + (Optional) Verbose logging, enabled when set to `true` + +``DISABLE_RELAY_TO_IRC=false`` + (Optional) Fully disables bridging messages from Telegram → IRC when set to `true` + +``DISABLE_RELAY_TO_TELEGRAM=false`` + (Optional) Fully disables bridging messages from IRC → Telegram when set to `true` ************ @@ -97,6 +116,9 @@ Message settings ``IRC_SEND_DOCUMENT=false`` Send documents and files from Telegram to IRC (`why is this false by default? `_) +``IRC_SEND_PHOTO=true`` + All photos which the Telegram Bot receives are uploaded to imgur, and an imgur-link is then posted to IRC + ``IRC_EDITED_PREFIX="(edited) "`` Prefix to prepend to messages when a user edits a Telegram message and it is resent to IRC @@ -171,6 +193,13 @@ Telegram settings ``SHOW_DISCONNECT_MESSAGE=true`` Sends a message to Telegram when the bot disconnects from the IRC side. +``PREFER_FIRSTNAME=false`` + Prefer users adjustable «First name» from Telegram, over their @usernames, when sending messages to IRC channel + (Fallback will still be the @username if first name is not available) + +``QUOTE_NICKNAME=false`` + Place IRC nickname in a blockquote section of the message to Telegram, instead of inline message prefix. + ************** Imgur settings ************** diff --git a/docs/user/quick-start.md b/docs/user/quick-start.md index 53e2fe9..3dc4238 100644 --- a/docs/user/quick-start.md +++ b/docs/user/quick-start.md @@ -101,17 +101,23 @@ If your IRC channel is on the Freenode IRC network, use these exact commands to 1. `/query ChanServ ACCESS #channel ADD *!*@freenode/staff/* +Aiotv` 1. `/query ChanServ ACCESS #channel ADD +V` -### Configure Imgur Image Upload (IIU) +### Adjust default Imgur Image Upload (IIU) -_By default_, TeleIRC uploads images sent to the Telegram group to [Imgur][7]. Since IRC does not support images, Imgur is an intermediary approach to sending pictures sent on Telegram over to IRC. -Note that images will be publicly visible on the Internet if the URL is known. -[See context][8] for why Imgur is enabled by default. + +> [!IMPORTANT] +> _By default_, all images the Telegram Bot reads are uploaded by TeleIRC to [Imgur][7]. +> [See context][8] for why Imgur upload is enabled by default. By default, TeleIRC uses the TeleIRC-registered Imgur API key. We highly recommend registering your own API key in high-traffic channels. Otherwise, API rate limiting can occur. +#### Optionally disable all Imgur image uploads from Telegram + +* Set `IRC_SEND_PHOTO` to `false` in your `.env` file + +#### Alternatively use your own Imgur API details To register your own Imgur API key, follow these steps: 1. Create an Imgur account @@ -132,15 +138,35 @@ There are two ways to deploy TeleIRC persistently: Containers are the easiest way to deploy TeleIRC. Dockerfiles and other deployment resources are available in ``deployments/``. -#### Build TeleIRC +Ensure you have [docker](https://www.docker.com/) installed. + +#### Build TeleIRC docker image -1. Ensure you have [docker](https://www.docker.com/) installed 1. Enter container deployment directory (`cd deployments/container`) 1. Build image (`./build_image.sh`) 1. Run container (`docker run teleirc:latest`) -**NOTE**: -**This deployment method assumes you have a complete .env file** +> [!NOTE] +> This deployment can optionally copy a standalone .env file + + +#### Run TeleIRC using Docker compose + +1. Enter container deployment directory (`cd deployments/container`) +1. Run service using `docker compose`: + +```bash +IRC_SERVER=chat.freenode.net \ +IRC_CHANNEL='#channelname' \ +IRC_BOT_NAME='teleirc' \ +TELEIRC_TOKEN='000000000:AAAAAAaAAa2AaAAaoAAAA-a_aaAAaAaaaAA' \ +TELEGRAM_CHAT_ID='-0000000000000' \ +docker compose up -d teleirc +``` + +> [!TIP] +> Instead you can also add `environment:` entries via `docker-compose.yml`, or pass a standalone `.env` file using the CLI: +> `docker compose --env-file ../../.env up --build -d teleirc` ### Run binary diff --git a/env.example b/env.example index c9282e7..46ebe51 100644 --- a/env.example +++ b/env.example @@ -2,6 +2,19 @@ # See the Config File Glossary for instructions. # https://docs.teleirc.com/en/latest/user/config-file-glossary/ +############################################################################### +# # +# General settings # +# # +############################################################################### + +#####----- Configuration settings -----##### +DEBUG=false +DISABLE_RELAY_TO_IRC=false +DISABLE_RELAY_TO_TELEGRAM=false + + + ############################################################################### # # # IRC configuration settings # @@ -45,6 +58,7 @@ IRC_PREFIX="<" IRC_SUFFIX=">" IRC_SEND_STICKER_EMOJI=true IRC_SEND_DOCUMENT=false +IRC_SEND_PHOTO=true IRC_EDITED_PREFIX="(edited) " IRC_MAX_MESSAGE_LENGTH=400 IRC_SHOW_ZWSP=true @@ -75,6 +89,9 @@ SHOW_NICK_MESSAGE=false SHOW_LEAVE_MESSAGE=false LEAVE_MESSAGE_ALLOW_LIST="" SHOW_DISCONNECT_MESSAGE=true +PREFER_FIRSTNAME=false +QUOTE_NICKNAME=false + ################################################################################ diff --git a/internal/config.go b/internal/config.go index 4d22cc9..2549889 100644 --- a/internal/config.go +++ b/internal/config.go @@ -3,6 +3,7 @@ package internal import ( "fmt" "os" + "path/filepath" "strings" "github.com/caarlos0/env/v6" @@ -28,7 +29,8 @@ type IRCSettings struct { BotName string `env:"IRC_BOT_REALNAME" envDefault:"Powered by TeleIRC "` BotNick string `env:"IRC_BOT_NAME,required" validate:"notempty"` SendStickerEmoji bool `env:"IRC_SEND_STICKER_EMOJI" envDefault:"true"` - SendDocument bool `env:"IRC_SEND_DOCUMENT" envDefault:"true"` + SendDocument bool `env:"IRC_SEND_DOCUMENT" envDefault:"false"` + SendPhoto bool `env:"IRC_SEND_PHOTO" envDefault:"true"` Prefix string `env:"IRC_PREFIX" envDefault:"<"` Suffix string `env:"IRC_SUFFIX" envDefault:">"` ShowJoinMessage bool `env:"IRC_SHOW_JOIN_MESSAGE" envDefault:"true"` @@ -65,6 +67,8 @@ type TelegramSettings struct { ShowNickMessage bool `env:"SHOW_NICK_MESSAGE" envDefault:"false"` ShowDisconnectMessage bool `env:"SHOW_DISCONNECT_MESSAGE" envDefault:"false"` MaxMessagePerMinute int `env:"MAX_MESSAGE_PER_MINUTE" envDefault:"20"` + PreferName bool `env:"PREFER_FIRSTNAME" envDefault:"false"` + QuoteNick bool `env:"QUOTE_NICKNAME" envDefault:"false"` } // ImgurSettings includes settings related to Imgur uploading for Telegram photos @@ -135,13 +139,33 @@ func LoadConfig(path string) (*Settings, error) { if err := validate.RegisterValidation("notempty", validateEmptyString); err != nil { return nil, err } - // Attempt to load environment variables from path if path was provided - if path != ".env" && path != "" { - if err := godotenv.Load(path); err != nil { - return nil, err + // If a path was provided, try to load it. + if path != "" { + if info, err := os.Stat(path); err == nil { + // If the path is a directory, look for /.env. + if info.IsDir() { + envFile := filepath.Join(path, defaultPath) + if _, err := os.Stat(envFile); err == nil { + if err := godotenv.Load(envFile); err != nil { + return nil, err + } + } + } else { + // path exists and is a file — attempt to load it + if err := godotenv.Load(path); err != nil { + return nil, err + } + } + } else { + // If the provided path does not exist, continue and rely on passed process ENV variables + if os.IsNotExist(err) { + warning.Printf("config path %q not provided or does not exist; continuing and using process environment variables", path) + } else { + return nil, err + } } } else if _, err := os.Stat(defaultPath); !os.IsNotExist(err) { - // Attempt to load from defaultPath if defaultPath exists + // Attempt to load from defaultPath if it exists if err := godotenv.Load(defaultPath); err != nil { return nil, err } diff --git a/internal/handlers/irc/handlers.go b/internal/handlers/irc/handlers.go index be77130..5289d25 100644 --- a/internal/handlers/irc/handlers.go +++ b/internal/handlers/irc/handlers.go @@ -2,6 +2,7 @@ package irc import ( "fmt" + "html" "regexp" "strings" @@ -129,7 +130,13 @@ func messageHandler(c ClientInterface) func(*girc.Client, girc.Event) { // Strips out ACTION word from text formatted = "* " + e.Source.Name + msg[7:len(msg)-1] } else { - formatted = c.IRCSettings().Prefix + e.Source.Name + c.IRCSettings().Suffix + " " + e.Params[1] + if c.TgSettings().QuoteNick { + ircNicknameFormatted := c.IRCSettings().Prefix + e.Source.Name + c.IRCSettings().Suffix + ircMessageNoHtml := regexp.MustCompile(`<.*?>`).ReplaceAllString(e.Params[1], "") + formatted = "
" + html.EscapeString(ircNicknameFormatted) + "
\n" + html.EscapeString(strings.NewReplacer(">", "", "<", "").Replace(ircMessageNoHtml)) + } else { + formatted = c.IRCSettings().Prefix + e.Source.Name + c.IRCSettings().Suffix + " " + e.Params[1] + } } if hasNoForwardPrefix(c, e.Params[1]) { diff --git a/internal/handlers/irc/irc.go b/internal/handlers/irc/irc.go index ccbccce..c2d6947 100644 --- a/internal/handlers/irc/irc.go +++ b/internal/handlers/irc/irc.go @@ -18,12 +18,13 @@ type Client struct { TelegramSettings *internal.TelegramSettings logger internal.DebugLogger sendToTg func(string) + disableTgRelay bool } /* NewClient returns a new IRCClient based on the provided settings */ -func NewClient(settings *internal.IRCSettings, telegramSettings *internal.TelegramSettings, logger internal.DebugLogger) Client { +func NewClient(settings *internal.IRCSettings, telegramSettings *internal.TelegramSettings, logger internal.DebugLogger, disableTgRelay bool) Client { logger.LogInfo("Creating new IRC bot client...") client := girc.New(girc.Config{ Server: settings.Server, @@ -52,7 +53,7 @@ func NewClient(settings *internal.IRCSettings, telegramSettings *internal.Telegr } } - return Client{client, settings, telegramSettings, logger, nil} + return Client{client, settings, telegramSettings, logger, nil, disableTgRelay} } /* @@ -120,6 +121,10 @@ func (c Client) Logger() internal.DebugLogger { SendToTg sends a message to Telegram */ func (c Client) SendToTg(msg string) { + if c.disableTgRelay { + c.logger.LogDebug("Relaying to Telegram is disabled, skipping IRC message") + return + } c.sendToTg(msg) } diff --git a/internal/handlers/telegram/handler.go b/internal/handlers/telegram/handler.go index 021f49d..cf42a61 100644 --- a/internal/handlers/telegram/handler.go +++ b/internal/handlers/telegram/handler.go @@ -21,6 +21,13 @@ which handler to fire off */ func updateHandler(tg *Client, updates tgbotapi.UpdatesChannel) { for u := range updates { + // Don't process any messages that didn't come from the + // chat we're bridging + if u.Message.Chat.ID != tg.Settings.ChatID { + tg.logger.LogDebug("Ignored message from a telegram chat we're not bridging:", tg.Settings.ChatID) + continue + } + switch { case u.Message == nil: tg.logger.LogError("Missing message data") @@ -59,19 +66,13 @@ 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 := GetUsername(tg.IRCSettings.ShowZWSP, u.Message.From) + username := GetUsername(tg.IRCSettings.ShowZWSP, u.Message.From, tg.Settings.PreferName) formatted := "" if tg.IRCSettings.NoForwardPrefix != "" && strings.HasPrefix(u.Message.Text, tg.IRCSettings.NoForwardPrefix) { return } - // Don't forward messages to IRC that didn't come from the - // chat we're bridging - if u.Message.Chat.ID != tg.Settings.ChatID { - return - } - // Telegram user replied to a message if u.Message.ReplyToMessage != nil { replyHandler(tg, u) @@ -93,8 +94,8 @@ replyHandler handles when users reply to a Telegram message */ func replyHandler(tg *Client, u tgbotapi.Update) { replyText := strings.Trim(u.Message.ReplyToMessage.Text, " ") - username := GetUsername(tg.IRCSettings.ShowZWSP, u.Message.From) - replyUser := GetUsername(tg.IRCSettings.ShowZWSP, u.Message.ReplyToMessage.From) + username := GetUsername(tg.IRCSettings.ShowZWSP, u.Message.From, tg.Settings.PreferName) + replyUser := GetUsername(tg.IRCSettings.ShowZWSP, u.Message.ReplyToMessage.From, tg.Settings.PreferName) // Only show a portion of the reply text if replyTextAsRunes := []rune(replyText); len(replyTextAsRunes) > tg.Settings.ReplyLength { @@ -121,7 +122,7 @@ func joinHandler(tg *Client, users *[]tgbotapi.User) { if tg.IRCSettings.ShowJoinMessage { for _, user := range *users { user := user - username := GetFullUsername(tg.IRCSettings.ShowZWSP, &user) + username := GetFullUsername(tg.IRCSettings.ShowZWSP, &user, tg.Settings.PreferName) formatted := username + " has joined the Telegram Group!" tg.sendToIrc(formatted) } @@ -133,7 +134,7 @@ partHandler handles when users leave the Telegram group */ func partHandler(tg *Client, user *tgbotapi.User) { if tg.IRCSettings.ShowLeaveMessage { - username := GetFullUsername(tg.IRCSettings.ShowZWSP, user) + username := GetFullUsername(tg.IRCSettings.ShowZWSP, user, tg.Settings.PreferName) formatted := username + " has left the Telegram Group!" tg.sendToIrc(formatted) @@ -145,7 +146,12 @@ stickerHandler handles the Message.Sticker Telegram Object, which formats the Telegram message into its base Emoji unicode character. */ func stickerHandler(tg *Client, u tgbotapi.Update) { - username := GetUsername(tg.IRCSettings.ShowZWSP, u.Message.From) + if !tg.IRCSettings.SendStickerEmoji { + tg.logger.LogDebug("Skipped processing Message.Sticker. Reason: IRC_SEND_STICKER_EMOJI=false") + return + } + + username := GetUsername(tg.IRCSettings.ShowZWSP, u.Message.From, tg.Settings.PreferName) formatted := fmt.Sprintf("%s%s%s %s", tg.Settings.Prefix, username, @@ -159,8 +165,13 @@ photoHandler handles the Message.Photo Telegram object. Only acknowledges Photo exists, and sends notification to IRC */ func photoHandler(tg *Client, u tgbotapi.Update) { + if !tg.IRCSettings.SendPhoto { + tg.logger.LogDebug("Skipped processing Message.Photo. Reason: IRC_SEND_PHOTO=false") + return + } + link := uploadImage(tg, u) - username := GetUsername(tg.IRCSettings.ShowZWSP, u.Message.From) + username := GetUsername(tg.IRCSettings.ShowZWSP, u.Message.From, tg.Settings.PreferName) caption := u.Message.Caption if caption == "" { caption = "No caption provided." @@ -180,7 +191,12 @@ documentHandler receives a document object from Telegram, and sends a notification to IRC. */ func documentHandler(tg *Client, u *tgbotapi.Message) { - username := GetUsername(tg.IRCSettings.ShowZWSP, u.From) + if !tg.IRCSettings.SendDocument { + tg.logger.LogDebug("Skipped processing document object. Reason: IRC_SEND_DOCUMENT=false") + return + } + + username := GetUsername(tg.IRCSettings.ShowZWSP, u.From, tg.Settings.PreferName) formatted := username + " shared a file" if u.Document.MimeType != "" { formatted += " (" + u.Document.MimeType + ")" @@ -201,10 +217,11 @@ a notification to IRC. */ func locationHandler(tg *Client, u *tgbotapi.Message) { if !tg.IRCSettings.ShowLocationMessage { + tg.logger.LogDebug("Skipped processing location object. Reason: IRC_SHOW_LOCATION_MESSAGE=false") return } - username := GetUsername(tg.IRCSettings.ShowZWSP, u.From) + username := GetUsername(tg.IRCSettings.ShowZWSP, u.From, tg.Settings.PreferName) formatted := username + " shared their location: (" // f means do not use an exponent. diff --git a/internal/handlers/telegram/helpers.go b/internal/handlers/telegram/helpers.go index 19b65ab..6973593 100644 --- a/internal/handlers/telegram/helpers.go +++ b/internal/handlers/telegram/helpers.go @@ -5,9 +5,12 @@ import ( ) /* -GetUsername takes showZWSP condition and user then returns username with or without ​. +GetUsername takes showZWSP and preferFirstname conditions and user then returns First name or username with or without ​. */ -func GetUsername(showZWSP bool, u *tgbotapi.User) string { +func GetUsername(showZWSP bool, u *tgbotapi.User, preferFirstname bool) string { + if preferFirstname && u.FirstName != "" { + return u.FirstName + } if u.UserName == "" { return u.FirstName } @@ -18,9 +21,12 @@ func GetUsername(showZWSP bool, u *tgbotapi.User) string { } /* -GetFullUsername takes showZWSP condition and user then returns full username with or without ​. +GetFullUsername takes showZWSP and preferFirstname conditions and user then returns full name or username with or without ​. */ -func GetFullUsername(showZWSP bool, u *tgbotapi.User) string { +func GetFullUsername(showZWSP bool, u *tgbotapi.User, preferFirstname bool) string { + if preferFirstname && u.FirstName != "" { + return u.FirstName + } if u.UserName == "" { return u.FirstName } diff --git a/internal/handlers/telegram/telegram.go b/internal/handlers/telegram/telegram.go index 1479f86..c8618a8 100644 --- a/internal/handlers/telegram/telegram.go +++ b/internal/handlers/telegram/telegram.go @@ -11,20 +11,21 @@ Client contains information for the Telegram bridge, including the TelegramSettings needed to run the bot */ type Client struct { - api *tgbotapi.BotAPI - Settings *internal.TelegramSettings - IRCSettings *internal.IRCSettings - ImgurSettings *internal.ImgurSettings - logger internal.DebugLogger - sendToIrc func(string) + api *tgbotapi.BotAPI + Settings *internal.TelegramSettings + IRCSettings *internal.IRCSettings + ImgurSettings *internal.ImgurSettings + logger internal.DebugLogger + sendToIrc func(string) + disableIrcRelay bool } /* NewClient creates a new Telegram bot client */ -func NewClient(settings *internal.TelegramSettings, ircsettings *internal.IRCSettings, imgur *internal.ImgurSettings, tgapi *tgbotapi.BotAPI, logger internal.DebugLogger) *Client { +func NewClient(settings *internal.TelegramSettings, ircsettings *internal.IRCSettings, imgur *internal.ImgurSettings, tgapi *tgbotapi.BotAPI, logger internal.DebugLogger, disableIrcRelay bool) *Client { logger.LogInfo("Creating new Telegram bot client...") - return &Client{api: tgapi, Settings: settings, IRCSettings: ircsettings, ImgurSettings: imgur, logger: logger} + return &Client{api: tgapi, Settings: settings, IRCSettings: ircsettings, ImgurSettings: imgur, logger: logger, disableIrcRelay: disableIrcRelay} } /* @@ -33,6 +34,9 @@ SendMessage sends a message to the Telegram channel specified in the settings func (tg *Client) SendMessage(msg string) { newMsg := tgbotapi.NewMessage(tg.Settings.ChatID, "") newMsg.Text = msg + if tg.Settings.QuoteNick { + newMsg.ParseMode = "HTML" + } if _, err := tg.api.Send(newMsg); err != nil { var attempts int = 0 @@ -66,16 +70,23 @@ func (tg *Client) StartBot(errChan chan<- error, sendMessage func(string)) { tg.logger.LogInfo("Authorized on account", tg.api.Self.UserName) tg.sendToIrc = sendMessage - u := tgbotapi.NewUpdate(0) - u.Timeout = 60 + if !tg.disableIrcRelay { + u := tgbotapi.NewUpdate(0) + u.Timeout = 60 - updates, err := tg.api.GetUpdatesChan(u) - if err != nil { - errChan <- err - tg.logger.LogError(err) - } + updates, err := tg.api.GetUpdatesChan(u) + if err != nil { + errChan <- err + tg.logger.LogError(err) + return + } - updateHandler(tg, updates) + updateHandler(tg, updates) - errChan <- nil + errChan <- nil + } else { + tg.logger.LogInfo("Telegram -> IRC relay disabled, but Telegram bot remains active for IRC -> Telegram messages") + // Block forever to keep the goroutine alive + select {} + } }