Merge branch 'master' into adhoc

This commit is contained in:
Bohdan Horbeshko 2024-04-28 07:04:42 -04:00
commit 43f9603b88
10 changed files with 203 additions and 91 deletions

1
.gitignore vendored
View file

@ -4,3 +4,4 @@ sessions/
session.dat session.dat
session.dat.new session.dat.new
release/ release/
tdlib/

View file

@ -29,7 +29,7 @@ WORKDIR /src
RUN make ${MAKEOPTS} RUN make ${MAKEOPTS}
FROM scratch AS telegabber FROM scratch AS telegabber
COPY --from=build /src/telegabber /usr/local/bin/ COPY --from=build /src/release/telegabber /usr/local/bin/
ENTRYPOINT ["/usr/local/bin/telegabber"] ENTRYPOINT ["/usr/local/bin/telegabber"]
FROM scratch AS binaries FROM scratch AS binaries

View file

@ -6,7 +6,8 @@ VERSION := "v1.10.0-dev"
MAKEOPTS := "-j4" MAKEOPTS := "-j4"
all: all:
go build -ldflags "-X main.commit=${COMMIT}" -o telegabber mkdir -p release
go build -ldflags "-X main.commit=${COMMIT}" -o release/telegabber
test: test:
go test -v ./config ./ ./telegram ./xmpp ./xmpp/gateway ./persistence ./telegram/formatter ./badger go test -v ./config ./ ./telegram ./xmpp ./xmpp/gateway ./persistence ./telegram/formatter ./badger
@ -16,3 +17,9 @@ lint:
build_indocker: build_indocker:
docker build --build-arg "TD_COMMIT=${TD_COMMIT}" --build-arg "VERSION=${VERSION}" --build-arg "MAKEOPTS=${MAKEOPTS}" --output=release --target binaries . docker build --build-arg "TD_COMMIT=${TD_COMMIT}" --build-arg "VERSION=${VERSION}" --build-arg "MAKEOPTS=${MAKEOPTS}" --output=release --target binaries .
build_indocker_staging:
DOCKER_BUILDKIT=1 docker build --build-arg "TD_COMMIT=${TD_COMMIT}" --build-arg "MAKEOPTS=${MAKEOPTS}" --network host --output=release --target binaries -f staging.Dockerfile .
build_tdlib:
DOCKER_BUILDKIT=1 docker build --build-arg "TD_COMMIT=${TD_COMMIT}" --build-arg "MAKEOPTS=${MAKEOPTS}" --output=tdlib --target binaries -f tdlib.Dockerfile .

46
staging.Dockerfile Normal file
View file

@ -0,0 +1,46 @@
FROM golang:1.19-bullseye AS base
RUN apt-get update
RUN apt-get install -y libssl-dev cmake build-essential gperf libz-dev make git php
FROM base AS tdlib
ARG TD_COMMIT
ARG MAKEOPTS
RUN git clone https://github.com/tdlib/td /src/
RUN git -C /src/ checkout "${TD_COMMIT}"
RUN mkdir build
WORKDIR /build/
RUN cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/compiled/ /src/
RUN cmake --build . --target prepare_cross_compiling ${MAKEOPTS}
WORKDIR /src/
RUN php SplitSource.php
WORKDIR /build/
RUN cmake --build . ${MAKEOPTS}
RUN make install
FROM base AS cache
ARG VERSION
COPY --from=tdlib /compiled/ /usr/local/
WORKDIR /src
RUN go env -w GOCACHE=/go-cache
RUN go env -w GOMODCACHE=/gomod-cache
RUN --mount=type=cache,target=/gomod-cache \
--mount=type=bind,source=./,target=/src \
go mod download
FROM cache AS build
ARG MAKEOPTS
WORKDIR /src
RUN --mount=type=bind,source=./,target=/src,rw \
--mount=type=cache,target=/go-cache \
--mount=type=cache,target=/gomod-cache \
--mount=type=cache,destination=/src/release \
make ${MAKEOPTS}
FROM build AS release
RUN --mount=type=cache,destination=/src/release \
cp /src/release/telegabber /
FROM scratch AS binaries
COPY --from=release /telegabber /

23
tdlib.Dockerfile Normal file
View file

@ -0,0 +1,23 @@
FROM golang:1.19-bullseye AS base
RUN apt-get update
RUN apt-get install -y libssl-dev cmake build-essential gperf libz-dev make git php
FROM base AS tdlib
ARG TD_COMMIT
ARG MAKEOPTS
RUN git clone https://github.com/tdlib/td /src/
RUN git -C /src/ checkout "${TD_COMMIT}"
RUN mkdir build
WORKDIR /build/
RUN cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/compiled/ /src/
RUN cmake --build . --target prepare_cross_compiling ${MAKEOPTS}
WORKDIR /src/
RUN php SplitSource.php
WORKDIR /build/
RUN cmake --build . ${MAKEOPTS}
RUN make install
FROM scratch AS binaries
COPY --from=tdlib /compiled/ /

View file

@ -362,16 +362,15 @@ func (c *Client) ProcessTransportCommand(cmdline string, resource string) (strin
return notOnline, false return notOnline, false
} }
for _, id := range c.cache.ChatsKeys() {
c.unsubscribe(id)
}
_, err := c.client.LogOut() _, err := c.client.LogOut()
if err != nil { if err != nil {
c.forceClose()
return errors.Wrap(err, "Logout error").Error(), false return errors.Wrap(err, "Logout error").Error(), false
} }
for _, id := range c.cache.ChatsKeys() {
c.unsubscribe(id)
}
c.Session.Login = "" c.Session.Login = ""
// cancel auth // cancel auth
case "cancelauth": case "cancelauth":
@ -466,16 +465,6 @@ func (c *Client) ProcessTransportCommand(cmdline string, resource string) (strin
if gateway.MessageOutgoingPermissionVersion == 0 && args[0] == "carbons" && args[1] == "true" { if gateway.MessageOutgoingPermissionVersion == 0 && args[0] == "carbons" && args[1] == "true" {
return "The server did not allow to enable carbons", false return "The server did not allow to enable carbons", false
} }
if !c.Session.RawMessages && args[0] == "nativeedits" && args[1] == "true" {
return "nativeedits only works with rawmessages as of yet, enable it first", false
}
if c.Session.NativeEdits && args[0] == "rawmessages" && args[1] == "false" {
_, err := c.Session.Set("nativeedits", "false")
if err != nil {
return err.Error(), false
}
msg = "Automatically disabling nativeedits too...\n"
}
value, err := c.Session.Set(args[0], args[1]) value, err := c.Session.Set(args[0], args[1])
if err != nil { if err != nil {

View file

@ -56,8 +56,10 @@ func (c *Client) cleanTempFile(path string) {
} }
func (c *Client) sendMarker(chatId, messageId int64, typ gateway.MarkerType) { func (c *Client) sendMarker(chatId, messageId int64, typ gateway.MarkerType) {
if xmppId, err := gateway.IdsDB.GetByTgIds(c.Session.Login, c.jid, chatId, messageId); err == nil { xmppId, err := gateway.IdsDB.GetByTgIds(c.Session.Login, c.jid, chatId, messageId)
resource := c.getFromOutbox(xmppId) if err != nil {
xmppId = strconv.FormatInt(messageId, 10)
}
var stringType string var stringType string
if typ == gateway.MarkerTypeReceived { if typ == gateway.MarkerTypeReceived {
@ -67,19 +69,15 @@ func (c *Client) sendMarker(chatId, messageId int64, typ gateway.MarkerType) {
} }
log.WithFields(log.Fields{ log.WithFields(log.Fields{
"xmppId": xmppId, "xmppId": xmppId,
"resource": resource,
}).Debugf("marker: %s", stringType) }).Debugf("marker: %s", stringType)
if resource != "" {
gateway.SendMessageMarker( gateway.SendMessageMarker(
c.jid+"/"+resource, c.jid,
strconv.FormatInt(chatId, 10), strconv.FormatInt(chatId, 10),
c.xmpp, c.xmpp,
typ, typ,
xmppId, xmppId,
) )
}
}
} }
func (c *Client) updateHandler() { func (c *Client) updateHandler() {
@ -306,38 +304,48 @@ func (c *Client) updateMessageContent(update *client.UpdateMessageContent) {
} }
if ok && lastXmppId == xmppId { if ok && lastXmppId == xmppId {
replaceId = xmppId replaceId = xmppId
message, err := c.client.GetMessage(&client.GetMessageRequest{
ChatId: update.ChatId,
MessageId: update.MessageId,
})
if err == nil {
isCarbon = c.isCarbonsEnabled() && message.IsOutgoing
} else {
log.Errorf("No message %v/%v found, cannot reliably determine if it's a carbon", update.ChatId, update.MessageId)
}
} else { } else {
log.Infof("Mismatching message ids: %v %v, falling back to separate edit message", lastXmppId, xmppId) log.Infof("Mismatching message ids: %v %v, falling back to separate edit message", lastXmppId, xmppId)
} }
} }
text := formatter.Format( message, messageErr := c.client.GetMessage(&client.GetMessageRequest{
textContent.Text.Text, ChatId: update.ChatId,
textContent.Text.Entities, MessageId: update.MessageId,
markupFunction, })
) var prefix string
if messageErr == nil {
isCarbon = c.isCarbonsEnabled() && message.IsOutgoing
// reply correction support in clients is suboptimal yet, so cut them out for now
prefix, _ = c.messageToPrefix(message, "", "", true)
} else {
log.Errorf("No message %v/%v found, cannot reliably determine if it's a carbon", update.ChatId, update.MessageId)
}
var text strings.Builder
if replaceId == "" { if replaceId == "" {
var editChar string var editChar string
if c.Session.AsciiArrows { if c.Session.AsciiArrows {
editChar = "e " editChar = "e"
} else { } else {
editChar = "✎ " editChar = "✎"
} }
text = editChar + fmt.Sprintf("%v | %s", update.MessageId, text) text.WriteString(fmt.Sprintf("%s %v | ", editChar, update.MessageId))
} else if prefix != "" {
text.WriteString(prefix)
text.WriteString(c.getPrefixSeparator(update.ChatId))
} }
text.WriteString(formatter.Format(
textContent.Text.Text,
textContent.Text.Entities,
markupFunction,
))
sChatId := strconv.FormatInt(update.ChatId, 10)
for _, jid := range jids { for _, jid := range jids {
gateway.SendMessage(jid, strconv.FormatInt(update.ChatId, 10), text, "e"+sId, c.xmpp, nil, replaceId, isCarbon, false) gateway.SendMessage(jid, sChatId, text.String(), "e"+sId, c.xmpp, nil, replaceId, isCarbon, false)
} }
} }
} }
@ -373,6 +381,8 @@ func (c *Client) updateMessageSendSucceeded(update *client.UpdateMessageSendSucc
log.Errorf("failed to replace %v with %v: %v", update.OldMessageId, update.Message.Id, err.Error()) log.Errorf("failed to replace %v with %v: %v", update.OldMessageId, update.Message.Id, err.Error())
} }
c.updateLastMessageHash(update.Message.ChatId, update.Message.Id, update.Message.Content)
c.sendMarker(update.Message.ChatId, update.Message.Id, gateway.MarkerTypeReceived) c.sendMarker(update.Message.ChatId, update.Message.Id, gateway.MarkerTypeReceived)
// clean uploaded files // clean uploaded files

View file

@ -967,7 +967,7 @@ func (c *Client) isCarbonsEnabled() bool {
return gateway.MessageOutgoingPermissionVersion > 0 && c.Session.Carbons return gateway.MessageOutgoingPermissionVersion > 0 && c.Session.Carbons
} }
func (c *Client) messageToPrefix(message *client.Message, previewString string, fileString string) (string, *gateway.Reply) { func (c *Client) messageToPrefix(message *client.Message, previewString string, fileString string, suppressReply bool) (string, *gateway.Reply) {
isPM, err := c.IsPM(message.ChatId) isPM, err := c.IsPM(message.ChatId)
if err != nil { if err != nil {
log.Errorf("Could not determine if chat is PM: %v", err) log.Errorf("Could not determine if chat is PM: %v", err)
@ -1005,10 +1005,14 @@ func (c *Client) messageToPrefix(message *client.Message, previewString string,
} }
// reply to // reply to
var reply *gateway.Reply
if !suppressReply {
preview := true preview := true
reply, tgReply := c.getMessageReply(message, preview, false) gwReply, tgReply := c.getMessageReply(message, preview, false)
if tgReply != nil { if tgReply != nil {
reply = gwReply
var replyStart, replyEnd int var replyStart, replyEnd int
if len(prefix) > 0 { if len(prefix) > 0 {
@ -1028,6 +1032,7 @@ func (c *Client) messageToPrefix(message *client.Message, previewString string,
reply.End = uint64(replyEnd) reply.End = uint64(replyEnd)
} }
} }
}
if message.ForwardInfo != nil { if message.ForwardInfo != nil {
prefix = append(prefix, "fwd: "+c.formatOrigin(message.ForwardInfo.Origin)) prefix = append(prefix, "fwd: "+c.formatOrigin(message.ForwardInfo.Origin))
@ -1060,6 +1065,17 @@ func (c *Client) ensureDownloadFile(file *client.File) *client.File {
return file return file
} }
// \n if it is groupchat and message is not empty
func (c *Client) getPrefixSeparator(chatId int64) string {
var separator string
if chatId < 0 {
separator = "\n"
} else if chatId > 0 {
separator = " | "
}
return separator
}
// ProcessIncomingMessage transfers a message to XMPP side and marks it as read on Telegram side // ProcessIncomingMessage transfers a message to XMPP side and marks it as read on Telegram side
func (c *Client) ProcessIncomingMessage(chatId int64, message *client.Message) { func (c *Client) ProcessIncomingMessage(chatId int64, message *client.Message) {
isCarbon := c.isCarbonsEnabled() && message.IsOutgoing isCarbon := c.isCarbonsEnabled() && message.IsOutgoing
@ -1103,21 +1119,15 @@ func (c *Client) ProcessIncomingMessage(chatId int64, message *client.Message) {
} else if !c.Session.RawMessages { } else if !c.Session.RawMessages {
var newText strings.Builder var newText strings.Builder
prefix, prefixReply := c.messageToPrefix(message, previewName, fileName) prefix, prefixReply := c.messageToPrefix(message, previewName, fileName, false)
reply = prefixReply reply = prefixReply
replyObtained = true replyObtained = true
newText.WriteString(prefix) newText.WriteString(prefix)
if text != "" { if text != "" {
// \n if it is groupchat and message is not empty
if prefix != "" { if prefix != "" {
if chatId < 0 { newText.WriteString(c.getPrefixSeparator(chatId))
newText.WriteString("\n")
} else if chatId > 0 {
newText.WriteString(" | ")
} }
}
newText.WriteString(text) newText.WriteString(text)
} }
text = newText.String() text = newText.String()

View file

@ -436,7 +436,7 @@ func TestMessageToPrefix1(t *testing.T) {
}, },
}, },
} }
prefix, gatewayReply := (&Client{Session: &persistence.Session{}}).messageToPrefix(&message, "", "") prefix, gatewayReply := (&Client{Session: &persistence.Session{}}).messageToPrefix(&message, "", "", false)
if prefix != "➡ 42 | fwd: ziz" { if prefix != "➡ 42 | fwd: ziz" {
t.Errorf("Wrong prefix: %v", prefix) t.Errorf("Wrong prefix: %v", prefix)
} }
@ -454,7 +454,7 @@ func TestMessageToPrefix2(t *testing.T) {
}, },
}, },
} }
prefix, gatewayReply := (&Client{Session: &persistence.Session{}}).messageToPrefix(&message, "y.jpg", "") prefix, gatewayReply := (&Client{Session: &persistence.Session{}}).messageToPrefix(&message, "y.jpg", "", false)
if prefix != "⬅ 56 | fwd: (zaz) | preview: y.jpg" { if prefix != "⬅ 56 | fwd: (zaz) | preview: y.jpg" {
t.Errorf("Wrong prefix: %v", prefix) t.Errorf("Wrong prefix: %v", prefix)
} }
@ -472,7 +472,7 @@ func TestMessageToPrefix3(t *testing.T) {
}, },
}, },
} }
prefix, gatewayReply := (&Client{Session: &persistence.Session{AsciiArrows: true}}).messageToPrefix(&message, "", "a.jpg") prefix, gatewayReply := (&Client{Session: &persistence.Session{AsciiArrows: true}}).messageToPrefix(&message, "", "a.jpg", false)
if prefix != "< 56 | fwd: (zuz) | file: a.jpg" { if prefix != "< 56 | fwd: (zuz) | file: a.jpg" {
t.Errorf("Wrong prefix: %v", prefix) t.Errorf("Wrong prefix: %v", prefix)
} }
@ -486,7 +486,7 @@ func TestMessageToPrefix4(t *testing.T) {
Id: 23, Id: 23,
IsOutgoing: true, IsOutgoing: true,
} }
prefix, gatewayReply := (&Client{Session: &persistence.Session{AsciiArrows: true}}).messageToPrefix(&message, "", "") prefix, gatewayReply := (&Client{Session: &persistence.Session{AsciiArrows: true}}).messageToPrefix(&message, "", "", false)
if prefix != "> 23" { if prefix != "> 23" {
t.Errorf("Wrong prefix: %v", prefix) t.Errorf("Wrong prefix: %v", prefix)
} }
@ -504,7 +504,7 @@ func TestMessageToPrefix5(t *testing.T) {
}, },
}, },
} }
prefix, gatewayReply := (&Client{Session: &persistence.Session{AsciiArrows: true}}).messageToPrefix(&message, "h.jpg", "a.jpg") prefix, gatewayReply := (&Client{Session: &persistence.Session{AsciiArrows: true}}).messageToPrefix(&message, "h.jpg", "a.jpg", false)
if prefix != "< 560 | fwd: (zyz) | preview: h.jpg | file: a.jpg" { if prefix != "< 560 | fwd: (zyz) | preview: h.jpg | file: a.jpg" {
t.Errorf("Wrong prefix: %v", prefix) t.Errorf("Wrong prefix: %v", prefix)
} }
@ -530,7 +530,7 @@ func TestMessageToPrefix6(t *testing.T) {
}, },
}, },
} }
prefix, gatewayReply := (&Client{Session: &persistence.Session{AsciiArrows: true}}).messageToPrefix(&message, "", "") prefix, gatewayReply := (&Client{Session: &persistence.Session{AsciiArrows: true}}).messageToPrefix(&message, "", "", false)
if prefix != "> 23 | reply: ziz @ unknown contact: TDlib instance is offline | tist uz iz" { if prefix != "> 23 | reply: ziz @ unknown contact: TDlib instance is offline | tist uz iz" {
t.Errorf("Wrong prefix: %v", prefix) t.Errorf("Wrong prefix: %v", prefix)
} }
@ -556,7 +556,7 @@ func TestMessageToPrefix7(t *testing.T) {
}, },
}, },
} }
prefix, gatewayReply := (&Client{Session: &persistence.Session{AsciiArrows: true}}).messageToPrefix(&message, "", "") prefix, gatewayReply := (&Client{Session: &persistence.Session{AsciiArrows: true}}).messageToPrefix(&message, "", "", false)
if prefix != "> 23 | reply: (zaz) @ unknown contact: TDlib instance is offline | tist" { if prefix != "> 23 | reply: (zaz) @ unknown contact: TDlib instance is offline | tist" {
t.Errorf("Wrong prefix: %v", prefix) t.Errorf("Wrong prefix: %v", prefix)
} }
@ -565,6 +565,32 @@ func TestMessageToPrefix7(t *testing.T) {
} }
} }
func TestMessageToPrefix8(t *testing.T) {
message := client.Message{
Id: 23,
ChatId: 42,
IsOutgoing: true,
ReplyTo: &client.MessageReplyToMessage{
ChatId: 41,
Content: &client.MessageText{
Text: &client.FormattedText{
Text: "tist",
},
},
Origin: &client.MessageOriginChannel{
AuthorSignature: "zuz",
},
},
}
prefix, gatewayReply := (&Client{Session: &persistence.Session{AsciiArrows: true}}).messageToPrefix(&message, "", "", true)
if prefix != "> 23" {
t.Errorf("Wrong prefix: %v", prefix)
}
if gatewayReply != nil {
t.Errorf("Reply is not nil: %v", gatewayReply)
}
}
func GetSenderIdEmpty(t *testing.T) { func GetSenderIdEmpty(t *testing.T) {
message := client.Message{} message := client.Message{}
senderId := (&Client{}).getMessageSenderId(&message) senderId := (&Client{}).getMessageSenderId(&message)

View file

@ -210,7 +210,7 @@ func HandleMessage(s xmpp.Sender, p stanza.Packet) {
} else { } else {
err = gateway.IdsDB.Set(session.Session.Login, bare, toID, tgMessageId, msg.Id) err = gateway.IdsDB.Set(session.Session.Login, bare, toID, tgMessageId, msg.Id)
if err == nil { if err == nil {
session.AddToOutbox(msg.Id, resource) // session.AddToOutbox(msg.Id, resource)
session.UpdateLastChatMessageId(toID, msg.Id) session.UpdateLastChatMessageId(toID, msg.Id)
} else { } else {
log.Errorf("Failed to save ids %v/%v %v", toID, tgMessageId, msg.Id) log.Errorf("Failed to save ids %v/%v %v", toID, tgMessageId, msg.Id)