Skip to content

Commit

Permalink
fix: msg format
Browse files Browse the repository at this point in the history
  • Loading branch information
akurilov committed Jun 1, 2024
1 parent 385b4f1 commit b8ae332
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 29 deletions.
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ func main() {
go b.Start()

// chats websub handler (subscriber)
hChats := chats.NewHandler(cfg.Api.Reader.Uri, fmtMsg, urlCallbackBase, svcReader, b)
hChats := chats.NewHandler(cfg.Api.Reader.Uri, fmtMsg, urlCallbackBase, svcReader, b, clientAwk, groupId)
r := gin.Default()
r.
Group(cfg.Api.Reader.CallBack.Path).
Expand Down
2 changes: 1 addition & 1 deletion scripts/cover.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/bin/bash

COVERAGE=$(cat cover.tmp)
THRESHOLD=5
THRESHOLD=10
if [[ ${COVERAGE} -lt ${THRESHOLD} ]]; \
then \
echo "FAILED: test coverage ${COVERAGE}% < ${THRESHOLD}%"; \
Expand Down
53 changes: 37 additions & 16 deletions service/chats/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@ import (
"errors"
"fmt"
apiHttpReader "github.com/awakari/bot-telegram/api/http/reader"
"github.com/awakari/bot-telegram/service"
"github.com/awakari/bot-telegram/service/messages"
"github.com/awakari/client-sdk-go/api"
"github.com/bytedance/sonic/utf8"
"github.com/cenkalti/backoff/v4"
ceProto "github.com/cloudevents/sdk-go/binding/format/protobuf/v2"
"github.com/cloudevents/sdk-go/binding/format/protobuf/v2/pb"
ce "github.com/cloudevents/sdk-go/v2/event"
"github.com/gin-gonic/gin"
"google.golang.org/grpc/metadata"
"gopkg.in/telebot.v3"
"net/http"
"reflect"
Expand All @@ -32,6 +35,8 @@ type handler struct {
urlCallbackBase string
svcReader apiHttpReader.Service
tgBot *telebot.Bot
clientAwk api.Client
groupId string
}

const keyHubChallenge = "hub.challenge"
Expand All @@ -45,13 +50,17 @@ func NewHandler(
urlCallbackBase string,
svcReader apiHttpReader.Service,
tgBot *telebot.Bot,
clientAwk api.Client,
groupId string,
) Handler {
return handler{
topicPrefixBase: topicPrefixBase,
format: format,
urlCallbackBase: urlCallbackBase,
svcReader: svcReader,
tgBot: tgBot,
clientAwk: clientAwk,
groupId: groupId,
}
}

Expand Down Expand Up @@ -105,6 +114,12 @@ func (h handler) DeliverMessages(ctx *gin.Context) {
return
}

var subDescr string
groupIdCtx := metadata.AppendToOutgoingContext(context.TODO(), service.KeyGroupId, h.groupId)
userId := service.PrefixUserId + chatIdRaw
sub, _ := h.clientAwk.ReadSubscription(groupIdCtx, userId, subId)
subDescr = sub.Description

defer ctx.Request.Body.Close()
var evts []*ce.Event
err = json.NewDecoder(ctx.Request.Body).Decode(&evts)
Expand All @@ -114,7 +129,7 @@ func (h handler) DeliverMessages(ctx *gin.Context) {
}

var countAck uint32
countAck, err = h.deliver(ctx, evts, subId, chatId)
countAck, err = h.deliver(ctx, evts, subId, subDescr, chatId)
if err == nil || countAck > 0 {
ctx.Writer.Header().Add(keyAckCount, strconv.FormatUint(uint64(countAck), 10))
ctx.Status(http.StatusOK)
Expand All @@ -125,7 +140,16 @@ func (h handler) DeliverMessages(ctx *gin.Context) {
return
}

func (h handler) deliver(ctx context.Context, evts []*ce.Event, subId string, chatId int64) (countAck uint32, err error) {
func (h handler) deliver(
ctx context.Context,
evts []*ce.Event,
subId string,
subDescr string,
chatId int64,
) (
countAck uint32,
err error,
) {
tgCtx := h.tgBot.NewContext(telebot.Update{
Message: &telebot.Message{
Chat: &telebot.Chat{
Expand All @@ -136,22 +160,19 @@ func (h handler) deliver(ctx context.Context, evts []*ce.Event, subId string, ch
for _, evt := range evts {
var evtProto *pb.CloudEvent
evtProto, err = ceProto.ToProto(evt)
dataTxt := string(evt.Data())
if utf8.ValidateString(dataTxt) {
if strings.HasPrefix(dataTxt, "\"") {
dataTxt = dataTxt[1:]
}
if strings.HasSuffix(dataTxt, "\"") {
dataTxt = dataTxt[:len(dataTxt)-1]
}
evtProto.Data = &pb.CloudEvent_TextData{
TextData: dataTxt,
}
var dataTxt string
if err == nil {
err = evt.DataAs(&dataTxt)
}
if err != nil {
break
}
tgMsg := h.format.Convert(evtProto, subId, messages.FormatModeHtml)
if err == nil && utf8.ValidateString(dataTxt) {
evtProto.Data = &pb.CloudEvent_TextData{
TextData: dataTxt,
}
}
tgMsg := h.format.Convert(evtProto, subId, subDescr, messages.FormatModeHtml)
err = tgCtx.Send(tgMsg, telebot.ModeHTML)
if err != nil {
switch err.(type) {
Expand All @@ -166,7 +187,7 @@ func (h handler) deliver(ctx context.Context, evts []*ce.Event, subId string, ch
return
}
fmt.Printf("Failed to send message %+v to chat %d in HTML mode, cause: %s (%s)\n", tgMsg, chatId, err, reflect.TypeOf(err))
tgMsg = h.format.Convert(evtProto, subId, messages.FormatModePlain)
tgMsg = h.format.Convert(evtProto, subId, subDescr, messages.FormatModePlain)
err = tgCtx.Send(tgMsg) // fallback: try to re-send as a plain text
}
}
Expand All @@ -176,7 +197,7 @@ func (h handler) deliver(ctx context.Context, evts []*ce.Event, subId string, ch
go h.handleFloodError(ctx, tgCtx, subId, chatId, err.(telebot.FloodError).RetryAfter)
default:
fmt.Printf("Failed to send message %+v in plain text mode, cause: %s\n", tgMsg, err)
tgMsg = h.format.Convert(evtProto, subId, messages.FormatModeRaw)
tgMsg = h.format.Convert(evtProto, subId, subDescr, messages.FormatModeRaw)
err = tgCtx.Send(tgMsg) // fallback: try to re-send as a raw text w/o file attachments
}
}
Expand Down
21 changes: 10 additions & 11 deletions service/messages/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ var htmlStripTags = bluemonday.
StrictPolicy().
AddSpaceWhenStrippingTag(true)

func (f Format) Convert(evt *pb.CloudEvent, subId string, mode FormatMode) (tgMsg any) {
func (f Format) Convert(evt *pb.CloudEvent, subId, subDescr string, mode FormatMode) (tgMsg any) {
fileTypeAttr, fileTypeFound := evt.Attributes[attrKeyFileType]
if fileTypeFound && mode != FormatModeRaw {
ft := FileType(fileTypeAttr.GetCeInteger())
Expand All @@ -44,27 +44,27 @@ func (f Format) Convert(evt *pb.CloudEvent, subId string, mode FormatMode) (tgMs
tgMsg = &telebot.Audio{
File: file,
Duration: int(evt.Attributes[attrKeyFileMediaDuration].GetCeInteger()),
Caption: f.convert(evt, subId, mode, false, false),
Caption: f.convert(evt, subId, subDescr, mode, false, false),
}
case FileTypeDocument:
tgMsg = &telebot.Document{
File: file,
Caption: f.convert(evt, subId, mode, false, false),
Caption: f.convert(evt, subId, subDescr, mode, false, false),
}
case FileTypeImage:
tgMsg = &telebot.Photo{
File: file,
Width: int(evt.Attributes[attrKeyFileImgWidth].GetCeInteger()),
Height: int(evt.Attributes[attrKeyFileImgHeight].GetCeInteger()),
Caption: f.convert(evt, subId, mode, false, false),
Caption: f.convert(evt, subId, subDescr, mode, false, false),
}
case FileTypeVideo:
tgMsg = &telebot.Video{
File: file,
Width: int(evt.Attributes[attrKeyFileImgWidth].GetCeInteger()),
Height: int(evt.Attributes[attrKeyFileImgHeight].GetCeInteger()),
Duration: int(evt.Attributes[attrKeyFileMediaDuration].GetCeInteger()),
Caption: f.convert(evt, subId, mode, false, false),
Caption: f.convert(evt, subId, subDescr, mode, false, false),
}
}
} else {
Expand All @@ -73,15 +73,15 @@ func (f Format) Convert(evt *pb.CloudEvent, subId string, mode FormatMode) (tgMs
case true:
// no need to truncate for telegram when message is from telegram
// no need to convert any other attributes except text and footer
tgMsg = f.convert(evt, subId, mode, false, true)
tgMsg = f.convert(evt, subId, subDescr, mode, false, true)
default:
tgMsg = f.convert(evt, subId, mode, true, true)
tgMsg = f.convert(evt, subId, subDescr, mode, true, true)
}
}
return
}

func (f Format) convert(evt *pb.CloudEvent, subId string, mode FormatMode, trunc, attrs bool) (txt string) {
func (f Format) convert(evt *pb.CloudEvent, subId, subDescr string, mode FormatMode, trunc, attrs bool) (txt string) {
if attrs {
txt += f.convertHeaderAttrs(evt, mode, trunc)
}
Expand Down Expand Up @@ -145,12 +145,11 @@ func (f Format) convert(evt *pb.CloudEvent, subId string, mode FormatMode, trunc
txt += obj + "\n\n"
}
//
subDetailsLink := "https://awakari.com/sub-details.html?id=" + subId
switch mode {
case FormatModeHtml:
txt += "<a href=\"" + subDetailsLink + "\">Subscription</a>\n\n"
txt += "Subscription: <a href=\"https://awakari.com/sub-details.html?id=" + subId + "\">" + subDescr + "</a>\n\n"
default:
txt += "Subscription: " + subDetailsLink + "\n\n"
txt += "Subscription: " + subDescr + "\n\n"
}
//
var attrsTxt string
Expand Down
81 changes: 81 additions & 0 deletions service/messages/format_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package messages

import (
"github.com/cloudevents/sdk-go/binding/format/protobuf/v2/pb"
"github.com/microcosm-cc/bluemonday"
"github.com/stretchr/testify/assert"
"google.golang.org/protobuf/types/known/timestamppb"
"testing"
"time"
)

func TestFormat_Convert(t *testing.T) {
// init events format, see https://core.telegram.org/bots/api#html-style for details
htmlPolicy := bluemonday.NewPolicy()
htmlPolicy.AllowStandardURLs()
htmlPolicy.
AllowAttrs("href").
OnElements("a")
htmlPolicy.AllowElements("b", "strong", "i", "em", "u", "ins", "s", "strike", "del", "code", "pre")
htmlPolicy.
AllowAttrs("class").
OnElements("span")
htmlPolicy.AllowURLSchemes("tg")
htmlPolicy.
AllowAttrs("emoji-ids").
OnElements("tg-emoji")
htmlPolicy.
AllowAttrs("class").
OnElements("code")
htmlPolicy.AllowDataURIImages()
fmtMsg := Format{
HtmlPolicy: htmlPolicy,
}
cases := map[string]struct {
in *pb.CloudEvent
out any
}{
"1": {
in: &pb.CloudEvent{
Id: "82f39262-5eb4-4f7f-9142-7c489d670907",
Source: "https://bbs.archlinux.org/extern.php?action=feed&fid=32&type=atom",
SpecVersion: "1.0",
Type: "com.awakari.api.permits.exhausted",
Attributes: map[string]*pb.CloudEventAttributeValue{
"time": {
Attr: &pb.CloudEventAttributeValue_CeTimestamp{
CeTimestamp: timestamppb.New(time.Date(2024, 5, 31, 23, 54, 00, 0, time.UTC)),
},
},
},
Data: &pb.CloudEvent_TextData{
TextData: `⚠ Daily publishing limit reached.
Increase your daily publication limit or nominate own sources for the dedicated limit.
If you did not publish messages, <a href="https://awakari.com/pub.html?own=true">check own publication sources</a> you added.`,
},
},
out: `⚠ Daily publishing limit reached.
Increase your daily publication limit or nominate own sources for the dedicated limit.
If you did not publish messages, <a href="https://awakari.com/pub.html?own=true" rel="nofollow">check own publication sources</a...
Subscription: <a href="https://awakari.com/sub-details.html?id=sub1">sub1 description</a>
<span class="tg-spoiler">id: 82f39262-5eb4-4f7f-9142-7c489d670907
source: https://bbs.archlinux.org/extern.php?action=feed&fid=32&type=atom
type: com.awakari.api.permits.exhausted
time: 2024-05-31T23:54:00Z
</span>
`,
},
}
for k, c := range cases {
t.Run(k, func(t *testing.T) {
out := fmtMsg.Convert(c.in, "sub1", "sub1 description", FormatModeHtml)
assert.Equal(t, c.out, out)
})
}
}

0 comments on commit b8ae332

Please sign in to comment.