From 8ef78ce3049668c64aae2a6cf9459c709acf0189 Mon Sep 17 00:00:00 2001 From: highesttt Date: Thu, 8 Jan 2026 16:25:28 -0600 Subject: [PATCH 1/8] =?UTF-8?q?chore:=20=F0=9F=A4=96=20Updated=20repo=20na?= =?UTF-8?q?me?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BREAKING CHANGE: 🧨 Updated Repo name --- .gitignore | 2 +- .pre-commit-config.yaml | 2 +- Dockerfile | 4 ++-- README.md | 6 +++--- build.sh | 2 +- cmd/mautrix-line/main.go | 6 +++--- docker-compose.yml | 16 ++++++++-------- docker-run.sh | 2 +- go.mod | 2 +- pkg/connector/client.go | 4 ++-- pkg/connector/connector.go | 6 +++--- pkg/e2ee/manager.go | 4 ++-- pkg/line/client.go | 6 +++--- pkg/line/secret/secret.go | 2 +- 14 files changed, 32 insertions(+), 32 deletions(-) diff --git a/.gitignore b/.gitignore index 05828c6..f47c14d 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,7 @@ *.db* *.log* -/mautrix-line* +/matrix-line* /start /docs diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 88e1b05..2eb7cf6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,7 +15,7 @@ repos: exclude: "pb\\.go$" args: - "-local" - - "github.com/highesttt/mautrix-line-messenger" + - "github.com/highesttt/matrix-line-messenger" - "-w" - id: go-vet-mod - id: go-staticcheck-repo-mod diff --git a/Dockerfile b/Dockerfile index 70a745b..1c46d8b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,7 +10,7 @@ COPY go.mod go.sum ./ RUN go mod download COPY . . -RUN go build -o mautrix-line ./cmd/mautrix-line +RUN go build -o matrix-line ./cmd/matrix-line FROM ${DOCKER_HUB}/alpine:3.22 @@ -19,7 +19,7 @@ ENV UID=1337 \ RUN apk add --no-cache ffmpeg su-exec ca-certificates bash jq curl yq-go olm nodejs -COPY --from=builder /build/mautrix-line /usr/bin/mautrix-line +COPY --from=builder /build/matrix-line /usr/bin/matrix-line COPY ./docker-run.sh /docker-run.sh RUN chmod +x /docker-run.sh ENV BRIDGEV2=1 diff --git a/README.md b/README.md index 59b12f7..c863750 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # matrix-line-messenger -[![Go Report Card](https://goreportcard.com/badge/github.com/highesttt/mautrix-line-messenger)](https://goreportcard.com/report/github.com/highesttt/mautrix-line-messenger) -![Languages](https://img.shields.io/github/languages/top/highesttt/mautrix-line-messenger.svg) -[![License](https://img.shields.io/github/license/highesttt/mautrix-line-messenger.svg)](LICENSE) +[![Go Report Card](https://goreportcard.com/badge/github.com/highesttt/matrix-line-messenger)](https://goreportcard.com/report/github.com/highesttt/matrix-line-messenger) +![Languages](https://img.shields.io/github/languages/top/highesttt/matrix-line-messenger.svg) +[![License](https://img.shields.io/github/license/highesttt/matrix-line-messenger.svg)](LICENSE) A Matrix bridge for LINE Messenger using mautrix-go.\ Based on the [mautrix-twilio](https://github.com/mautrix/twilio) bridge diff --git a/build.sh b/build.sh index 46256e9..62df5ef 100755 --- a/build.sh +++ b/build.sh @@ -1,4 +1,4 @@ #!/bin/sh MAUTRIX_VERSION=$(cat go.mod | grep 'maunium.net/go/mautrix ' | awk '{ print $2 }' | head -n1) GO_LDFLAGS="-s -w -X main.Tag=$(git describe --exact-match --tags 2>/dev/null) -X main.Commit=$(git rev-parse HEAD) -X 'main.BuildTime=`date -Iseconds`' -X 'maunium.net/go/mautrix.GoModVersion=$MAUTRIX_VERSION'" -go build -ldflags="$GO_LDFLAGS" ./cmd/mautrix-line "$@" +go build -ldflags="$GO_LDFLAGS" ./cmd/matrix-line "$@" diff --git a/cmd/mautrix-line/main.go b/cmd/mautrix-line/main.go index a19f858..e93f6b3 100644 --- a/cmd/mautrix-line/main.go +++ b/cmd/mautrix-line/main.go @@ -3,7 +3,7 @@ package main import ( "maunium.net/go/mautrix/bridgev2/matrix/mxmain" - "github.com/highesttt/mautrix-line-messenger/pkg/connector" + "github.com/highesttt/matrix-line-messenger/pkg/connector" ) // Information to find out exactly which commit the bridge was built from. @@ -16,9 +16,9 @@ var ( func main() { m := mxmain.BridgeMain{ - Name: "mautrix-line", + Name: "matrix-line", Description: "A Matrix-LINE bridge", - URL: "https://github.com/highesttt/mautrix-line-messenger", + URL: "https://github.com/highesttt/matrix-line-messenger", Version: "0.1.0", Connector: &connector.LineConnector{}, } diff --git a/docker-compose.yml b/docker-compose.yml index c5093f9..0f66147 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,10 +1,10 @@ -version: '3.8' +version: "3.8" services: - mautrix-line: - build: - context: . - dockerfile: Dockerfile - volumes: - - ./data:/data - restart: unless-stopped + matrix-line: + build: + context: . + dockerfile: Dockerfile + volumes: + - ./data:/data + restart: unless-stopped diff --git a/docker-run.sh b/docker-run.sh index 3bbca8b..af70fc4 100755 --- a/docker-run.sh +++ b/docker-run.sh @@ -4,7 +4,7 @@ if [[ -z "$GID" ]]; then GID="$UID" fi -BINARY_NAME=/usr/bin/mautrix-line +BINARY_NAME=/usr/bin/matrix-line function fixperms { chown -R $UID:$GID /data diff --git a/go.mod b/go.mod index 81da286..433f8bd 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/highesttt/mautrix-line-messenger +module github.com/highesttt/matrix-line-messenger go 1.24.0 diff --git a/pkg/connector/client.go b/pkg/connector/client.go index ca74445..1b92b12 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -37,8 +37,8 @@ import ( "github.com/rs/zerolog" - "github.com/highesttt/mautrix-line-messenger/pkg/e2ee" - "github.com/highesttt/mautrix-line-messenger/pkg/line" + "github.com/highesttt/matrix-line-messenger/pkg/e2ee" + "github.com/highesttt/matrix-line-messenger/pkg/line" ) type LineClient struct { diff --git a/pkg/connector/connector.go b/pkg/connector/connector.go index 80bc815..e5f2903 100644 --- a/pkg/connector/connector.go +++ b/pkg/connector/connector.go @@ -18,8 +18,8 @@ import ( "github.com/rs/zerolog/hlog" - "github.com/highesttt/mautrix-line-messenger/pkg/e2ee" - "github.com/highesttt/mautrix-line-messenger/pkg/line" + "github.com/highesttt/matrix-line-messenger/pkg/e2ee" + "github.com/highesttt/matrix-line-messenger/pkg/line" ) type LineConnector struct { @@ -63,7 +63,7 @@ func (lc *LineConnector) GetName() bridgev2.BridgeName { NetworkURL: "https://line.me", NetworkIcon: "https://www.google.com/s2/favicons?domain=line.me&sz=256", NetworkID: "line", - BeeperBridgeType: "github.com/highesttt/mautrix-line-messenger", + BeeperBridgeType: "github.com/highesttt/matrix-line-messenger", DefaultPort: 29322, } } diff --git a/pkg/e2ee/manager.go b/pkg/e2ee/manager.go index 0d53191..28f8e4e 100644 --- a/pkg/e2ee/manager.go +++ b/pkg/e2ee/manager.go @@ -12,8 +12,8 @@ import ( "strings" "sync" - gen "github.com/highesttt/mautrix-line-messenger/pkg" - "github.com/highesttt/mautrix-line-messenger/pkg/line" + gen "github.com/highesttt/matrix-line-messenger/pkg" + "github.com/highesttt/matrix-line-messenger/pkg/line" ) /* diff --git a/pkg/line/client.go b/pkg/line/client.go index b0227cf..803c324 100644 --- a/pkg/line/client.go +++ b/pkg/line/client.go @@ -12,9 +12,9 @@ import ( "strings" "time" - gen "github.com/highesttt/mautrix-line-messenger/pkg" - "github.com/highesttt/mautrix-line-messenger/pkg/line/password" - "github.com/highesttt/mautrix-line-messenger/pkg/line/secret" + gen "github.com/highesttt/matrix-line-messenger/pkg" + "github.com/highesttt/matrix-line-messenger/pkg/line/password" + "github.com/highesttt/matrix-line-messenger/pkg/line/secret" ) const ( diff --git a/pkg/line/secret/secret.go b/pkg/line/secret/secret.go index 08eb7ef..e39d7d2 100644 --- a/pkg/line/secret/secret.go +++ b/pkg/line/secret/secret.go @@ -3,7 +3,7 @@ package secret import ( "fmt" - gen "github.com/highesttt/mautrix-line-messenger/pkg" + gen "github.com/highesttt/matrix-line-messenger/pkg" ) type SecretResult struct { From 8d19dc2d66404974e6e90f4967a40a542a08680e Mon Sep 17 00:00:00 2001 From: highesttt Date: Thu, 8 Jan 2026 16:28:38 -0600 Subject: [PATCH 2/8] =?UTF-8?q?chore:=20=F0=9F=A4=96=20updated=20repo=20na?= =?UTF-8?q?me?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BREAKING CHANGE: 🧨 updated repo name --- cmd/{mautrix-line => matrix-line}/main.go | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename cmd/{mautrix-line => matrix-line}/main.go (100%) diff --git a/cmd/mautrix-line/main.go b/cmd/matrix-line/main.go similarity index 100% rename from cmd/mautrix-line/main.go rename to cmd/matrix-line/main.go From d7807131f9217b79c92cc5f820863abfe5294f95 Mon Sep 17 00:00:00 2001 From: highesttt Date: Thu, 8 Jan 2026 17:09:38 -0600 Subject: [PATCH 3/8] =?UTF-8?q?docs:=20=E2=9C=8F=EF=B8=8F=20Added=20instru?= =?UTF-8?q?ctions=20to=20readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/README.md b/README.md index c863750..90d5b67 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,12 @@ A Matrix bridge for LINE Messenger using mautrix-go.\ Based on the [mautrix-twilio](https://github.com/mautrix/twilio) bridge +## Known issues + +> [!NOTE] +> Messages sent to the LINE Bot using Beeper Desktop may appear as indefinitely sending.\ +> Use Beeper Mobile to send commands to the LINE Bot account after creating the chat with Beeper Desktop. + ## Roadmap - [x] Basic messaging (encrypted text messages) @@ -24,3 +30,48 @@ Based on the [mautrix-twilio](https://github.com/mautrix/twilio) bridge - [x] Media messages (images, videos, voice notes, files) - [ ] Sticker support +## How to Use + +1. Clone the repository: + + ```bash + git clone https://github.com/highesttt/matrix-line-messenger.git + cd matrix-line-messenger + ``` + +2. Create a `data` directory for configuration and data storage: + + ```bash + mkdir data + ``` + +3. Create a configuration file using [bbctl](https://github.com/beeper/bridge-manager): + + ```bash + bbctl c --type bridgev2 sh-line > config.yaml + ``` + +4. Move the generated `config.yaml` into the `data` directory: + + ```bash + mv config.yaml data/ + ``` + +5. Change `public_address` in `data/config.yaml` to your desired public address + +6. Build and run the bridge using Docker (use -d for detached mode): + + ```bash + docker compose up --build (-d) + ``` + + To run the bridge without rebuilding, use: + + ```bash + docker compose up (-d) + ``` + +## Login + +1. Open the Matrix client of your choice and start a chat with `@sh-line:your.matrix.homeserver.domain`. (For local beeper bridges, use `@sh-line:beeper.local`) +2. Send the command `login` and follow the instructions to log in to your LINE account. From 1a95588b03dc1bfac05c505f82c3e1a27f2acb34 Mon Sep 17 00:00:00 2001 From: highesttt Date: Thu, 8 Jan 2026 17:09:54 -0600 Subject: [PATCH 4/8] =?UTF-8?q?fix:=20=F0=9F=90=9B=20Incorrect=20network?= =?UTF-8?q?=20icon?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/connector/connector.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/connector/connector.go b/pkg/connector/connector.go index e5f2903..b382b50 100644 --- a/pkg/connector/connector.go +++ b/pkg/connector/connector.go @@ -61,7 +61,7 @@ func (lc *LineConnector) GetName() bridgev2.BridgeName { return bridgev2.BridgeName{ DisplayName: "LINE", NetworkURL: "https://line.me", - NetworkIcon: "https://www.google.com/s2/favicons?domain=line.me&sz=256", + NetworkIcon: "", NetworkID: "line", BeeperBridgeType: "github.com/highesttt/matrix-line-messenger", DefaultPort: 29322, @@ -137,7 +137,7 @@ var _ bridgev2.LoginProcessUserInput = (*LineEmailLogin)(nil) func (ll *LineEmailLogin) Start(ctx context.Context) (*bridgev2.LoginStep, error) { return &bridgev2.LoginStep{ Type: bridgev2.LoginStepTypeUserInput, - StepID: "fi.mau.line.enter_creds", + StepID: "dev.highest.matrix.line.enter_creds", Instructions: "Please enter your LINE email and password.", UserInputParams: &bridgev2.LoginUserInputParams{ Fields: []bridgev2.LoginInputDataField{ @@ -221,7 +221,7 @@ func (ll *LineEmailLogin) SubmitUserInput(ctx context.Context, input map[string] return &bridgev2.LoginStep{ Type: bridgev2.LoginStepTypeUserInput, - StepID: "fi.mau.line.wait_verification", + StepID: "dev.highest.matrix.line.wait_verification", Instructions: instructions, UserInputParams: &bridgev2.LoginUserInputParams{ Fields: []bridgev2.LoginInputDataField{ @@ -239,7 +239,7 @@ func (ll *LineEmailLogin) SubmitUserInput(ctx context.Context, input map[string] if res.Certificate != "" { return &bridgev2.LoginStep{ Type: bridgev2.LoginStepTypeUserInput, - StepID: "fi.mau.line.enter_pin", + StepID: "dev.highest.matrix.line.enter_pin", Instructions: fmt.Sprintf("Please open the LINE app on your mobile device and enter this PIN code: **%s**\n\nAfter entering the code, click 'Continue' below.", res.Certificate), UserInputParams: &bridgev2.LoginUserInputParams{ Fields: []bridgev2.LoginInputDataField{ @@ -327,7 +327,7 @@ func (ll *LineEmailLogin) finishLogin(ctx context.Context, res *line.LoginResult return &bridgev2.LoginStep{ Type: bridgev2.LoginStepTypeComplete, - StepID: "fi.mau.line.complete", + StepID: "dev.highest.matrix.line.complete", Instructions: "Successfully logged in", CompleteParams: &bridgev2.LoginCompleteParams{UserLoginID: ul.ID, UserLogin: ul}, }, nil From 7025c93b57abed44d590576b7324c77112eb135b Mon Sep 17 00:00:00 2001 From: highesttt Date: Thu, 8 Jan 2026 18:53:53 -0600 Subject: [PATCH 5/8] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20Replies=20from=20LIN?= =?UTF-8?q?E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/connector/client.go | 86 +++++++++++++++++++++++++++++++++-------- pkg/line/methods.go | 15 +++---- pkg/line/structs.go | 4 ++ 3 files changed, 81 insertions(+), 24 deletions(-) diff --git a/pkg/connector/client.go b/pkg/connector/client.go index c076310..730be5e 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -34,6 +34,7 @@ import ( "maunium.net/go/mautrix/bridgev2/simplevent" "maunium.net/go/mautrix/bridgev2/status" "maunium.net/go/mautrix/event" + "maunium.net/go/mautrix/id" "github.com/rs/zerolog" @@ -876,6 +877,7 @@ func (lc *LineClient) queueIncomingMessage(msg *line.Message, opType int) { Data: *msg, ID: networkid.MessageID(msg.ID), ConvertMessageFunc: func(ctx context.Context, portal *bridgev2.Portal, intent bridgev2.MatrixAPI, data line.Message) (*bridgev2.ConvertedMessage, error) { + replyRelatesTo := lc.resolveReplyRelatesTo(ctx, &data) // Handle Images if data.ContentType == 1 { // Image oid := data.ContentMetadata["OID"] @@ -936,10 +938,11 @@ func (lc *LineClient) queueIncomingMessage(msg *line.Message, opType int) { { Type: event.EventMessage, Content: &event.MessageEventContent{ - MsgType: event.MsgImage, - Body: "image.jpg", - URL: mxc, - File: file, + MsgType: event.MsgImage, + Body: "image.jpg", + URL: mxc, + File: file, + RelatesTo: replyRelatesTo, }, }, }, @@ -1081,11 +1084,12 @@ func (lc *LineClient) queueIncomingMessage(msg *line.Message, opType int) { { Type: event.EventMessage, Content: &event.MessageEventContent{ - MsgType: event.MsgVideo, - Body: fileName, - URL: mxc, - File: file, - Info: videoInfo, + MsgType: event.MsgVideo, + Body: fileName, + URL: mxc, + File: file, + Info: videoInfo, + RelatesTo: replyRelatesTo, }, }, }, @@ -1194,6 +1198,7 @@ func (lc *LineClient) queueIncomingMessage(msg *line.Message, opType int) { MimeType: mimeType, Size: len(fileData), }, + RelatesTo: replyRelatesTo, }, }, }, @@ -1248,14 +1253,15 @@ func (lc *LineClient) queueIncomingMessage(msg *line.Message, opType int) { { Type: event.EventMessage, Content: &event.MessageEventContent{ - MsgType: event.MsgImage, - Body: stkTxt, + MsgType: event.MsgFile, + Body: "sticker.png", URL: mxc, File: file, Info: &event.FileInfo{ MimeType: "image/png", Size: len(stkData), }, + RelatesTo: replyRelatesTo, }, }, }, @@ -1271,8 +1277,9 @@ func (lc *LineClient) queueIncomingMessage(msg *line.Message, opType int) { { Type: event.EventMessage, Content: &event.MessageEventContent{ - MsgType: event.MsgText, - Body: stkTxt, + MsgType: event.MsgText, + Body: stkTxt, + RelatesTo: replyRelatesTo, }, }, }, @@ -1285,8 +1292,9 @@ func (lc *LineClient) queueIncomingMessage(msg *line.Message, opType int) { { Type: event.EventMessage, Content: &event.MessageEventContent{ - MsgType: event.MsgText, - Body: unwrappedText, + MsgType: event.MsgText, + Body: unwrappedText, + RelatesTo: replyRelatesTo, }, }, }, @@ -1633,6 +1641,38 @@ func (lc *LineClient) ensurePeerKeyForMessage(ctx context.Context, msg *line.Mes } } +// resolveReplyRelatesTo looks up the Matrix event ID for a replied-to LINE message. +func (lc *LineClient) resolveReplyRelatesTo(ctx context.Context, data *line.Message) *event.RelatesTo { + if data == nil { + return nil + } + + relatedID := data.RelatedMessageID + if relatedID == "" && data.ContentMetadata != nil { + relatedID = data.ContentMetadata["message_relation_server_message_id"] + } + + if relatedID == "" { + return nil + } + + if data.MessageRelationType != 0 && data.MessageRelationType != 3 { + return nil + } + + dbMsg, err := lc.UserLogin.Bridge.DB.Message.GetPartByID(ctx, lc.UserLogin.ID, networkid.MessageID(relatedID), "") + if err != nil { + lc.UserLogin.Bridge.Log.Debug().Err(err).Str("related_msg_id", relatedID).Msg("Failed to lookup reply target") + return nil + } + if dbMsg == nil || dbMsg.MXID == "" { + lc.UserLogin.Bridge.Log.Debug().Str("related_msg_id", relatedID).Msg("No Matrix event found for reply target") + return nil + } + + return &event.RelatesTo{InReplyTo: &event.InReplyTo{EventID: dbMsg.MXID}} +} + func (lc *LineClient) HandleMatrixMessage(ctx context.Context, msg *bridgev2.MatrixMessage) (*bridgev2.MatrixMessageResponse, error) { if lc.E2EE == nil { return nil, fmt.Errorf("E2EE not initialized; cannot send") @@ -1986,6 +2026,17 @@ func (lc *LineClient) HandleMatrixMessage(ctx context.Context, msg *bridgev2.Mat Chunks: chunks, } + if msg.ReplyTo != nil { + dbMsg, err := lc.UserLogin.Bridge.DB.Message.GetPartByMXID(ctx, id.EventID(msg.ReplyTo.ID)) + if err == nil && dbMsg != nil && dbMsg.ID != "" { + lineMsg.RelatedMessageID = string(dbMsg.ID) + lineMsg.MessageRelationType = 3 + lineMsg.RelatedMessageServiceCode = 1 + } else { + lc.UserLogin.Bridge.Log.Warn().Str("reply_to_mxid", string(msg.ReplyTo.ID)).Msg("Failed to resolve reply target to LINE ID") + } + } + reqSeq := int(now % 1_000_000_000) lc.reqSeqMu.Lock() if lc.sentReqSeqs == nil { @@ -1994,13 +2045,14 @@ func (lc *LineClient) HandleMatrixMessage(ctx context.Context, msg *bridgev2.Mat lc.sentReqSeqs[reqSeq] = time.Now() lc.reqSeqMu.Unlock() - if err := client.SendMessage(int64(reqSeq), lineMsg); err != nil { + sentMsg, err := client.SendMessage(int64(reqSeq), lineMsg) + if err != nil { return nil, err } return &bridgev2.MatrixMessageResponse{ DB: &database.Message{ - ID: networkid.MessageID(lineMsg.ID), + ID: networkid.MessageID(sentMsg.ID), SenderID: makeUserID(string(lc.UserLogin.ID)), Timestamp: time.UnixMilli(now), }, diff --git a/pkg/line/methods.go b/pkg/line/methods.go index e59366b..7872e43 100644 --- a/pkg/line/methods.go +++ b/pkg/line/methods.go @@ -308,22 +308,23 @@ func (c *Client) GetE2EEPublicKey(mid string, keyVersion, keyID int) (*E2EEPubli return parseE2EEPublicKey(wrapper.Data) } -func (c *Client) SendMessage(reqSeq int64, msg *Message) error { +func (c *Client) SendMessage(reqSeq int64, msg *Message) (*Message, error) { resp, err := c.callRPC("TalkService", "sendMessage", reqSeq, msg) if err != nil { - return err + return nil, err } var wrapper struct { - Code int `json:"code"` - Message string `json:"message"` + Code int `json:"code"` + Message string `json:"message"` + Data *Message `json:"data"` } if err := json.Unmarshal(resp, &wrapper); err != nil { - return err + return nil, err } if wrapper.Code != 0 { - return fmt.Errorf("sendMessage failed: %s", wrapper.Message) + return nil, fmt.Errorf("sendMessage failed: %s", wrapper.Message) } - return nil + return wrapper.Data, nil } // SendChatChecked sends a read receipt for a message in a chat diff --git a/pkg/line/structs.go b/pkg/line/structs.go index 6d91423..9dc83bf 100644 --- a/pkg/line/structs.go +++ b/pkg/line/structs.go @@ -79,6 +79,10 @@ type Message struct { Text string `json:"text,omitempty"` Chunks []string `json:"chunks,omitempty"` + + RelatedMessageID string `json:"relatedMessageId,omitempty"` + MessageRelationType int `json:"messageRelationType,omitempty"` + RelatedMessageServiceCode int `json:"relatedMessageServiceCode,omitempty"` } // E2EEPublicKey represents the peer key returned by negotiateE2EEPublicKey From 8456a4bc64a84db90a57bc8a6374bbc86f511d8b Mon Sep 17 00:00:00 2001 From: highesttt Date: Thu, 8 Jan 2026 19:11:28 -0600 Subject: [PATCH 6/8] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20Replies=20from=20Mat?= =?UTF-8?q?rix=20client?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/connector/client.go | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/pkg/connector/client.go b/pkg/connector/client.go index 730be5e..6ca3e0c 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -34,7 +34,6 @@ import ( "maunium.net/go/mautrix/bridgev2/simplevent" "maunium.net/go/mautrix/bridgev2/status" "maunium.net/go/mautrix/event" - "maunium.net/go/mautrix/id" "github.com/rs/zerolog" @@ -2026,17 +2025,26 @@ func (lc *LineClient) HandleMatrixMessage(ctx context.Context, msg *bridgev2.Mat Chunks: chunks, } + var relatedMsg *database.Message + if msg.ReplyTo != nil { - dbMsg, err := lc.UserLogin.Bridge.DB.Message.GetPartByMXID(ctx, id.EventID(msg.ReplyTo.ID)) - if err == nil && dbMsg != nil && dbMsg.ID != "" { - lineMsg.RelatedMessageID = string(dbMsg.ID) - lineMsg.MessageRelationType = 3 - lineMsg.RelatedMessageServiceCode = 1 - } else { - lc.UserLogin.Bridge.Log.Warn().Str("reply_to_mxid", string(msg.ReplyTo.ID)).Msg("Failed to resolve reply target to LINE ID") + relatedMsg = msg.ReplyTo + } else if msg.Content.RelatesTo != nil && msg.Content.RelatesTo.InReplyTo != nil { + replyToMXID := msg.Content.RelatesTo.InReplyTo.EventID + if replyToMXID != "" { + dbMsg, err := lc.UserLogin.Bridge.DB.Message.GetPartByMXID(ctx, replyToMXID) + if err == nil && dbMsg != nil { + relatedMsg = dbMsg + } } } + if relatedMsg != nil && relatedMsg.ID != "" && !strings.HasPrefix(string(relatedMsg.ID), "local-") { + lineMsg.RelatedMessageID = string(relatedMsg.ID) + lineMsg.MessageRelationType = 3 + lineMsg.RelatedMessageServiceCode = 1 + } + reqSeq := int(now % 1_000_000_000) lc.reqSeqMu.Lock() if lc.sentReqSeqs == nil { From 16cd907f16b4ff69473681654afc5d0edb9672df Mon Sep 17 00:00:00 2001 From: highesttt Date: Thu, 8 Jan 2026 19:12:27 -0600 Subject: [PATCH 7/8] =?UTF-8?q?docs:=20=E2=9C=8F=EF=B8=8F=20Updated=20road?= =?UTF-8?q?map=20in=20readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9b60197..cc0eb90 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Based on the [mautrix-twilio](https://github.com/mautrix/twilio) bridge - [x] Read receipts - [ ] Typing indicators - [ ] Reaction support -- [ ] Reply support +- [x] Reply support - [ ] Prefetch chats - [x] Group chats - [x] Media messages (images, videos, voice notes, files) From 9224c7ad41189013fa818246364aaa9e2317313b Mon Sep 17 00:00:00 2001 From: highesttt Date: Thu, 8 Jan 2026 19:23:51 -0600 Subject: [PATCH 8/8] =?UTF-8?q?chore:=20=F0=9F=A4=96=20Changed=20message?= =?UTF-8?q?=20type=20for=20stickers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updated readme Step 6 --- README.md | 4 ++-- pkg/connector/client.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index cc0eb90..a862819 100644 --- a/README.md +++ b/README.md @@ -62,13 +62,13 @@ Based on the [mautrix-twilio](https://github.com/mautrix/twilio) bridge 6. Build and run the bridge using Docker (use -d for detached mode): ```bash - docker compose up --build (-d) + docker compose up --build -d ``` To run the bridge without rebuilding, use: ```bash - docker compose up (-d) + docker compose up -d ``` ## Login diff --git a/pkg/connector/client.go b/pkg/connector/client.go index 6ca3e0c..d41db57 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -1252,7 +1252,7 @@ func (lc *LineClient) queueIncomingMessage(msg *line.Message, opType int) { { Type: event.EventMessage, Content: &event.MessageEventContent{ - MsgType: event.MsgFile, + MsgType: event.MsgImage, Body: "sticker.png", URL: mxc, File: file,