Skip to content

Commit 32c1638

Browse files
rnonstulir
andauthored
handle*: add support for avatar changes (#81)
Co-authored-by: Tulir Asokan <[email protected]>
1 parent 9f82b34 commit 32c1638

File tree

7 files changed

+99
-0
lines changed

7 files changed

+99
-0
lines changed

pkg/connector/handlematrix.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"context"
2121
"errors"
2222
"fmt"
23+
"net/http"
2324

2425
"github.com/google/uuid"
2526
"github.com/rs/zerolog"
@@ -48,6 +49,7 @@ var (
4849
_ bridgev2.TypingHandlingNetworkAPI = (*TwitterClient)(nil)
4950
_ bridgev2.ChatViewingNetworkAPI = (*TwitterClient)(nil)
5051
_ bridgev2.DeleteChatHandlingNetworkAPI = (*TwitterClient)(nil)
52+
_ bridgev2.RoomAvatarHandlingNetworkAPI = (*TwitterClient)(nil)
5153
)
5254

5355
var _ bridgev2.TransactionIDGeneratingNetwork = (*TwitterConnector)(nil)
@@ -250,3 +252,41 @@ func (tc *TwitterClient) HandleMatrixDeleteChat(ctx context.Context, chat *bridg
250252
reqQuery := payload.DMRequestQuery{}.Default()
251253
return tc.client.DeleteConversation(ctx, conversationID, &reqQuery)
252254
}
255+
256+
func (tc *TwitterClient) HandleMatrixRoomAvatar(ctx context.Context, msg *bridgev2.MatrixRoomAvatar) (bool, error) {
257+
if msg.Portal.RoomType == database.RoomTypeDM {
258+
return false, errors.New("cannot set room avatar for DM")
259+
}
260+
261+
if msg.Content.URL != "" || msg.Content.MSC3414File != nil {
262+
data, err := msg.Portal.Bridge.Bot.DownloadMedia(ctx, msg.Content.URL, msg.Content.MSC3414File)
263+
if err != nil {
264+
return false, fmt.Errorf("failed to download avatar: %w", err)
265+
}
266+
267+
var mediaType string
268+
if msg.Content.Info != nil {
269+
mediaType = msg.Content.Info.MimeType
270+
} else {
271+
mediaType = http.DetectContentType(data)
272+
}
273+
274+
uploadMediaParams := &payload.UploadMediaQuery{
275+
MediaType: mediaType,
276+
}
277+
uploadedMediaResponse, err := tc.client.UploadMedia(ctx, uploadMediaParams, data)
278+
if err != nil {
279+
return false, err
280+
}
281+
282+
updateAvatarParams := &payload.DMRequestQuery{
283+
AvatarID: uploadedMediaResponse.MediaIDString,
284+
}
285+
err = tc.client.UpdateConversationAvatar(ctx, string(msg.Portal.ID), updateAvatarParams)
286+
if err != nil {
287+
return false, err
288+
}
289+
return true, nil
290+
}
291+
return false, errors.New("avatar not found")
292+
}

pkg/connector/handletwit.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,23 @@ func (tc *TwitterClient) HandleTwitterEvent(rawEvt types.TwitterEvent, inbox *re
181181
},
182182
}
183183
return tc.userLogin.QueueRemoteEvent(portalUpdateRemoteEvent).Success
184+
case *types.ConversationAvatarUpdate:
185+
chatInfo := &bridgev2.ChatInfo{
186+
Avatar: makeAvatar(tc.client, evt.ConversationAvatarImageHttps),
187+
}
188+
success := tc.userLogin.QueueRemoteEvent(&simplevent.ChatInfoChange{
189+
EventMeta: simplevent.EventMeta{
190+
Type: bridgev2.RemoteEventChatInfoChange,
191+
Sender: tc.MakeEventSender(evt.ByUserID),
192+
PortalKey: tc.makePortalKeyFromInbox(evt.ConversationID, inbox),
193+
StreamOrder: methods.ParseSnowflakeInt(evt.ID),
194+
Timestamp: methods.ParseSnowflake(evt.ID),
195+
},
196+
ChatInfoChange: &bridgev2.ChatInfoChange{
197+
ChatInfo: chatInfo,
198+
},
199+
}).Success
200+
return success
184201
case *types.ConversationMetadataUpdate:
185202
tc.client.Logger.Warn().Any("data", evt).Msg("Unhandled conversation metadata update event")
186203
return true

pkg/twittermeow/data/endpoints/endpoints.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ const (
2929
SEARCH_TYPEAHEAD_URL = BASE_URL + "/i/api/1.1/search/typeahead.json"
3030
DM_PERMISSIONS_URL = BASE_URL + "/i/api/1.1/dm/permissions.json"
3131
DELETE_CONVERSATION_URL = BASE_URL + "/i/api/1.1/dm/conversation/%s/delete.json"
32+
UPDATE_CONVERSATION_AVATAR_URL = BASE_URL + "/i/api/1.1/dm/conversation/%s/update_avatar.json"
3233
PIN_CONVERSATION_URL = BASE_URL + "/i/api/graphql/o0aymgGiJY-53Y52YSUGVA/DMPinnedInboxAppend_Mutation"
3334
UNPIN_CONVERSATION_URL = BASE_URL + "/i/api/graphql/_TQxP2Rb0expwVP9ktGrTQ/DMPinnedInboxDelete_Mutation"
3435
GET_PINNED_CONVERSATIONS_URL = BASE_URL + "/i/api/graphql/_gBQBgClVuMQb8efxWkbbQ/DMPinnedInboxQuery"

pkg/twittermeow/data/payload/form.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ const (
4949
)
5050

5151
type DMRequestQuery struct {
52+
AvatarID string `url:"avatar_id,omitempty"`
5253
ActiveConversationID string `url:"active_conversation_id,omitempty"`
5354
Cursor string `url:"cursor,omitempty"`
5455
Count int `url:"count,omitempty"`

pkg/twittermeow/data/types/event.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ func (*ConversationDelete) isTwitterEvent() {}
2222
func (*ConversationJoin) isTwitterEvent() {}
2323
func (*ParticipantsJoin) isTwitterEvent() {}
2424
func (*ParticipantsLeave) isTwitterEvent() {}
25+
func (*ConversationAvatarUpdate) isTwitterEvent() {}
2526
func (*ConversationMetadataUpdate) isTwitterEvent() {}
2627
func (*ConversationNameUpdate) isTwitterEvent() {}
2728
func (*ConversationRead) isTwitterEvent() {}
@@ -52,6 +53,7 @@ type twitterEventContainer struct {
5253
ConversationJoin *ConversationJoin `json:"join_conversation,omitempty"`
5354
ParticipantsJoin *ParticipantsJoin `json:"participants_join,omitempty"`
5455
ParticipantsLeave *ParticipantsLeave `json:"participants_leave,omitempty"`
56+
ConversationAvatarUpdate *ConversationAvatarUpdate `json:"conversation_avatar_update,omitempty"`
5557
ConversationMetadataUpdate *ConversationMetadataUpdate `json:"conversation_metadata_update,omitempty"`
5658
ConversationNameUpdate *ConversationNameUpdate `json:"conversation_name_update,omitempty"`
5759
ConversationRead *ConversationRead `json:"conversation_read,omitempty"`
@@ -86,6 +88,8 @@ func (rte *RawTwitterEvent) Parse() (TwitterEvent, map[string]any, error) {
8688
return tec.ParticipantsJoin, nil, nil
8789
case tec.ParticipantsLeave != nil:
8890
return tec.ParticipantsLeave, nil, nil
91+
case tec.ConversationAvatarUpdate != nil:
92+
return tec.ConversationAvatarUpdate, nil, nil
8993
case tec.ConversationMetadataUpdate != nil:
9094
return tec.ConversationMetadataUpdate, nil, nil
9195
case tec.ConversationNameUpdate != nil:

pkg/twittermeow/data/types/messaging.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,16 @@ type ConversationRead struct {
5454
LastReadEventID string `json:"last_read_event_id,omitempty"`
5555
}
5656

57+
type ConversationAvatarUpdate struct {
58+
ID string `json:"id,omitempty"`
59+
Time string `json:"time,omitempty"`
60+
AffectsSort bool `json:"affects_sort,omitempty"`
61+
ConversationID string `json:"conversation_id,omitempty"`
62+
ConversationAvatarImageHttps string `json:"conversation_avatar_image_https,omitempty"`
63+
Avatar Avatar `json:"avatar,omitempty"`
64+
ByUserID string `json:"by_user_id,omitempty"`
65+
}
66+
5767
type ConversationMetadataUpdate struct {
5868
ID string `json:"id,omitempty"`
5969
Time string `json:"time,omitempty"`

pkg/twittermeow/messaging.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,3 +348,29 @@ func (c *Client) React(ctx context.Context, reactionPayload *payload.ReactionAct
348348
data := response.ReactionResponse{}
349349
return &data, json.Unmarshal(respBody, &data)
350350
}
351+
352+
func (c *Client) UpdateConversationAvatar(ctx context.Context, conversationID string, payload *payload.DMRequestQuery) error {
353+
encodedQueryBody, err := payload.Encode()
354+
if err != nil {
355+
return err
356+
}
357+
358+
resp, respBody, err := c.makeAPIRequest(ctx, apiRequestOpts{
359+
URL: fmt.Sprintf(endpoints.UPDATE_CONVERSATION_AVATAR_URL, conversationID),
360+
Method: http.MethodPost,
361+
WithClientUUID: true,
362+
Body: encodedQueryBody,
363+
Referer: endpoints.BASE_MESSAGES_URL,
364+
Origin: endpoints.BASE_URL,
365+
ContentType: types.ContentTypeForm,
366+
})
367+
if err != nil {
368+
return err
369+
}
370+
371+
if resp.StatusCode > 204 {
372+
return fmt.Errorf("failed to update conversation avatar id=%s (status_code=%d, response_body=%s)", conversationID, resp.StatusCode, string(respBody))
373+
}
374+
375+
return nil
376+
}

0 commit comments

Comments
 (0)