|
80 | 80 | _ bridgev2.DeleteChatHandlingNetworkAPI = (*TelegramClient)(nil) |
81 | 81 | _ bridgev2.RoomNameHandlingNetworkAPI = (*TelegramClient)(nil) |
82 | 82 | _ bridgev2.RoomAvatarHandlingNetworkAPI = (*TelegramClient)(nil) |
| 83 | + _ bridgev2.MembershipHandlingNetworkAPI = (*TelegramClient)(nil) |
83 | 84 | ) |
84 | 85 |
|
85 | 86 | func getMediaFilename(content *event.MessageEventContent) (filename string) { |
@@ -382,7 +383,10 @@ func (tc *TelegramClient) transferMediaToTelegram(ctx context.Context, content * |
382 | 383 | }, nil |
383 | 384 | } |
384 | 385 |
|
385 | | -func (tc *TelegramClient) humaniseSendError(err error) bridgev2.MessageStatus { |
| 386 | +func (tc *TelegramClient) humaniseSendError(err error) error { |
| 387 | + if err == nil { |
| 388 | + return nil |
| 389 | + } |
386 | 390 | status := bridgev2.WrapErrorInStatus(err). |
387 | 391 | WithErrorReason(event.MessageStatusNetworkError). |
388 | 392 | WithMessage(humanise.Error(err)) |
@@ -1296,3 +1300,141 @@ func (tc *TelegramClient) HandleMatrixRoomAvatar(ctx context.Context, msg *bridg |
1296 | 1300 | return false, fmt.Errorf("unsupported peer type %s for changing room avatar", peerType) |
1297 | 1301 | } |
1298 | 1302 | } |
| 1303 | + |
| 1304 | +func wrapUnsupportedError(err error) bridgev2.MessageStatus { |
| 1305 | + return bridgev2.WrapErrorInStatus(err). |
| 1306 | + WithErrorReason(event.MessageStatusUnsupported). |
| 1307 | + WithIsCertain(true). |
| 1308 | + WithSendNotice(false) |
| 1309 | +} |
| 1310 | + |
| 1311 | +func (tc *TelegramClient) HandleMatrixMembership(ctx context.Context, msg *bridgev2.MatrixMembershipChange) (*bridgev2.MatrixMembershipResult, error) { |
| 1312 | + if (msg.Type.IsSelf && msg.OrigSender != nil) || msg.Type == bridgev2.ProfileChange { |
| 1313 | + return nil, nil |
| 1314 | + } |
| 1315 | + chatPeerType, chatID, _, err := ids.ParsePortalID(msg.Portal.ID) |
| 1316 | + if err != nil { |
| 1317 | + return nil, fmt.Errorf("failed to parse portal ID: %w", err) |
| 1318 | + } |
| 1319 | + var inputChannel *tg.InputChannel |
| 1320 | + switch chatPeerType { |
| 1321 | + case ids.PeerTypeUser: |
| 1322 | + return nil, nil |
| 1323 | + case ids.PeerTypeChannel: |
| 1324 | + if accessHash, err := tc.ScopedStore.GetAccessHash(ctx, ids.PeerTypeChannel, chatID); err != nil { |
| 1325 | + return nil, fmt.Errorf("failed to get access hash for channel: %w", err) |
| 1326 | + } else { |
| 1327 | + inputChannel = &tg.InputChannel{ChannelID: chatID, AccessHash: accessHash} |
| 1328 | + } |
| 1329 | + case ids.PeerTypeChat: |
| 1330 | + // ok |
| 1331 | + default: |
| 1332 | + return nil, wrapUnsupportedError(fmt.Errorf("unsupported chat peer type %s for membership changes", chatPeerType)) |
| 1333 | + } |
| 1334 | + var targetPeerType ids.PeerType |
| 1335 | + var targetUserID int64 |
| 1336 | + switch target := msg.Target.(type) { |
| 1337 | + case *bridgev2.UserLogin: |
| 1338 | + targetPeerType = ids.PeerTypeUser |
| 1339 | + targetUserID, err = ids.ParseUserLoginID(target.ID) |
| 1340 | + case *bridgev2.Ghost: |
| 1341 | + targetPeerType, targetUserID, err = ids.ParseUserID(target.ID) |
| 1342 | + default: |
| 1343 | + return nil, wrapUnsupportedError(fmt.Errorf("unknown membership target type %T", msg.Target)) |
| 1344 | + } |
| 1345 | + if err != nil { |
| 1346 | + return nil, fmt.Errorf("failed to parse target login ID: %w", err) |
| 1347 | + } |
| 1348 | + targetAccessHash, err := tc.ScopedStore.GetAccessHash(ctx, targetPeerType, targetUserID) |
| 1349 | + if err != nil { |
| 1350 | + return nil, fmt.Errorf("failed to get access hash for target: %w", err) |
| 1351 | + } |
| 1352 | + var targetInputPeer tg.InputPeerClass |
| 1353 | + var targetInputUser *tg.InputUser |
| 1354 | + switch targetPeerType { |
| 1355 | + case ids.PeerTypeUser: |
| 1356 | + targetInputPeer = &tg.InputPeerUser{UserID: targetUserID, AccessHash: targetAccessHash} |
| 1357 | + targetInputUser = &tg.InputUser{UserID: targetUserID, AccessHash: targetAccessHash} |
| 1358 | + case ids.PeerTypeChannel: |
| 1359 | + targetInputPeer = &tg.InputPeerChannel{ChannelID: targetUserID, AccessHash: targetAccessHash} |
| 1360 | + default: |
| 1361 | + return nil, wrapUnsupportedError(fmt.Errorf("unsupported target peer type %s for membership changes", targetPeerType)) |
| 1362 | + } |
| 1363 | + if chatPeerType == ids.PeerTypeChannel { |
| 1364 | + switch msg.Type { |
| 1365 | + case bridgev2.Kick, bridgev2.RejectKnock, bridgev2.RevokeInvite, bridgev2.BanLeft, bridgev2.BanJoined, bridgev2.BanInvited, bridgev2.BanKnocked: |
| 1366 | + _, err = tc.client.API().ChannelsEditBanned(ctx, &tg.ChannelsEditBannedRequest{ |
| 1367 | + Channel: inputChannel, |
| 1368 | + Participant: targetInputPeer, |
| 1369 | + BannedRights: tg.ChatBannedRights{ViewMessages: true}, |
| 1370 | + }) |
| 1371 | + if err != nil { |
| 1372 | + err = tc.humaniseSendError(err) |
| 1373 | + } else if msg.Type == bridgev2.Kick { |
| 1374 | + // Reset permissions to default (telegram doesn't have a dedicated kick method) |
| 1375 | + _, err = tc.client.API().ChannelsEditBanned(ctx, &tg.ChannelsEditBannedRequest{ |
| 1376 | + Channel: inputChannel, |
| 1377 | + Participant: targetInputPeer, |
| 1378 | + BannedRights: tg.ChatBannedRights{}, |
| 1379 | + }) |
| 1380 | + if err != nil { |
| 1381 | + err = tc.humaniseSendError(err) |
| 1382 | + return nil, fmt.Errorf("failed to unban user to emulate kick: %w", err) |
| 1383 | + } |
| 1384 | + } |
| 1385 | + case bridgev2.Unban: |
| 1386 | + _, err = tc.client.API().ChannelsEditBanned(ctx, &tg.ChannelsEditBannedRequest{ |
| 1387 | + Channel: inputChannel, |
| 1388 | + Participant: targetInputPeer, |
| 1389 | + BannedRights: tg.ChatBannedRights{}, |
| 1390 | + }) |
| 1391 | + err = tc.humaniseSendError(err) |
| 1392 | + case bridgev2.Invite, bridgev2.AcceptKnock: |
| 1393 | + if targetInputUser == nil { |
| 1394 | + return nil, wrapUnsupportedError(fmt.Errorf("can't invite non-user peer type %s", targetPeerType)) |
| 1395 | + } |
| 1396 | + _, err = tc.client.API().ChannelsInviteToChannel(ctx, &tg.ChannelsInviteToChannelRequest{ |
| 1397 | + Channel: inputChannel, |
| 1398 | + Users: []tg.InputUserClass{targetInputUser}, |
| 1399 | + }) |
| 1400 | + err = tc.humaniseSendError(err) |
| 1401 | + case bridgev2.Join, bridgev2.Knock: |
| 1402 | + _, err = tc.client.API().ChannelsJoinChannel(ctx, inputChannel) |
| 1403 | + err = tc.humaniseSendError(err) |
| 1404 | + case bridgev2.Leave, bridgev2.RetractKnock, bridgev2.RejectInvite: |
| 1405 | + _, err = tc.client.API().ChannelsLeaveChannel(ctx, inputChannel) |
| 1406 | + err = tc.humaniseSendError(err) |
| 1407 | + default: |
| 1408 | + err = wrapUnsupportedError(fmt.Errorf("unsupported channel membership change type %s -> %s", msg.Type.From, msg.Type.To)) |
| 1409 | + } |
| 1410 | + } else { |
| 1411 | + switch msg.Type { |
| 1412 | + case bridgev2.Kick, bridgev2.RejectKnock, bridgev2.RevokeInvite, bridgev2.BanLeft, bridgev2.BanJoined, bridgev2.BanInvited, bridgev2.BanKnocked, |
| 1413 | + bridgev2.Leave, bridgev2.RetractKnock, bridgev2.RejectInvite: |
| 1414 | + _, err = tc.client.API().MessagesDeleteChatUser(ctx, &tg.MessagesDeleteChatUserRequest{ |
| 1415 | + ChatID: chatID, |
| 1416 | + UserID: targetInputUser, |
| 1417 | + }) |
| 1418 | + err = tc.humaniseSendError(err) |
| 1419 | + case bridgev2.Invite, bridgev2.AcceptKnock: |
| 1420 | + if targetInputUser == nil { |
| 1421 | + return nil, wrapUnsupportedError(fmt.Errorf("can't invite non-user peer type %s", targetPeerType)) |
| 1422 | + } |
| 1423 | + _, err = tc.client.API().MessagesAddChatUser(ctx, &tg.MessagesAddChatUserRequest{ |
| 1424 | + ChatID: chatID, |
| 1425 | + UserID: targetInputUser, |
| 1426 | + FwdLimit: 50, |
| 1427 | + }) |
| 1428 | + err = tc.humaniseSendError(err) |
| 1429 | + case bridgev2.Join, bridgev2.Knock: |
| 1430 | + // TODO could maybe join if there's a saved invite link in the bridge db? |
| 1431 | + fallthrough |
| 1432 | + case bridgev2.Unban: |
| 1433 | + // There's no way to unban in minigroups |
| 1434 | + fallthrough |
| 1435 | + default: |
| 1436 | + err = wrapUnsupportedError(fmt.Errorf("unsupported minigroup membership change type %s -> %s", msg.Type.From, msg.Type.To)) |
| 1437 | + } |
| 1438 | + } |
| 1439 | + return nil, err |
| 1440 | +} |
0 commit comments