Files
mautrix-telegram/pkg/connector/ids/ids.go
T
2025-06-27 20:03:37 -07:00

229 lines
6.1 KiB
Go

// mautrix-telegram - A Matrix-Telegram puppeting bridge.
// Copyright (C) 2025 Sumner Evans
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package ids
import (
"fmt"
"strconv"
"strings"
"maunium.net/go/mautrix/bridgev2/networkid"
"go.mau.fi/mautrix-telegram/pkg/gotd/tg"
)
func MakeUserID(userID int64) networkid.UserID {
return networkid.UserID(strconv.FormatInt(userID, 10))
}
func MakeChannelUserID(channelID int64) networkid.UserID {
return networkid.UserID("channel-" + strconv.FormatInt(channelID, 10))
}
func ParseUserID(userID networkid.UserID) (PeerType, int64, error) {
peerType := PeerTypeUser
rawUserID := string(userID)
if strings.HasPrefix(string(userID), "channel-") {
peerType = PeerTypeChannel
rawUserID = strings.TrimPrefix(rawUserID, "channel-")
}
id, err := strconv.ParseInt(rawUserID, 10, 64)
return peerType, id, err
}
func ParseUserLoginID(userID networkid.UserLoginID) (int64, error) {
return strconv.ParseInt(string(userID), 10, 64)
}
func MakeUserLoginID(userID int64) networkid.UserLoginID {
return networkid.UserLoginID(strconv.FormatInt(userID, 10))
}
func GetMessageIDFromMessage(message tg.MessageClass) networkid.MessageID {
var peer tg.PeerClass
switch typedMsg := message.(type) {
case *tg.MessageEmpty:
peer, _ = typedMsg.GetPeerID()
case *tg.Message:
peer = typedMsg.GetPeerID()
case *tg.MessageService:
peer = typedMsg.GetPeerID()
default:
panic(fmt.Sprintf("unexpected message type %T", message))
}
return MakeMessageID(peer, message.GetID())
}
func MakeMessageID(rawChatID any, messageID int) networkid.MessageID {
var channelID int64
switch typedChatID := rawChatID.(type) {
case networkid.PortalKey:
peerType, entityID, _ := ParsePortalID(typedChatID.ID)
if peerType == PeerTypeChannel {
channelID = entityID
}
case *tg.PeerChannel:
channelID = typedChatID.ChannelID
case int64:
channelID = typedChatID
case *tg.PeerUser, *tg.PeerChat:
// No channel ID
case nil:
// Also no channel ID
default:
panic(fmt.Sprintf("unexpected chat ID type %T", rawChatID))
}
if channelID != 0 {
return networkid.MessageID(fmt.Sprintf("%d.%d", channelID, messageID))
}
return networkid.MessageID(fmt.Sprintf("%d", messageID))
}
func MakePaginationCursorID(messageID int) networkid.PaginationCursor {
return networkid.PaginationCursor(strconv.Itoa(messageID))
}
func ParseMessageID(networkID networkid.MessageID) (channelID int64, messageID int, err error) {
parts := strings.Split(string(networkID), ".")
if len(parts) == 1 {
messageID, err = strconv.Atoi(parts[0])
} else if len(parts) == 2 {
channelID, err = strconv.ParseInt(parts[0], 10, 64)
if err != nil {
err = fmt.Errorf("failed to parse chat ID: %w", err)
return
}
messageID, err = strconv.Atoi(parts[1])
} else {
err = fmt.Errorf("invalid number of parts in message ID")
}
return
}
type PeerType string
const (
PeerTypeUser PeerType = "user"
PeerTypeChat PeerType = "chat"
PeerTypeChannel PeerType = "channel"
)
func PeerTypeFromByte(pt byte) (PeerType, error) {
switch pt {
case 0x01:
return PeerTypeUser, nil
case 0x02:
return PeerTypeChat, nil
case 0x03:
return PeerTypeChannel, nil
default:
return "", fmt.Errorf("unknown peer type %d", pt)
}
}
func (pt PeerType) AsByte() byte {
switch pt {
case PeerTypeUser:
return 0x01
case PeerTypeChat:
return 0x02
case PeerTypeChannel:
return 0x03
default:
panic(fmt.Errorf("unknown peer type %s", pt))
}
}
func (pt PeerType) InternalAsPortalKey(chatID int64, receiver networkid.UserLoginID) networkid.PortalKey {
portalKey := networkid.PortalKey{
ID: networkid.PortalID(fmt.Sprintf("%s:%d", pt, chatID)),
}
if pt == PeerTypeUser || pt == PeerTypeChat {
portalKey.Receiver = receiver
}
return portalKey
}
func GetChatID(peer tg.PeerClass) int64 {
switch v := peer.(type) {
case *tg.PeerUser:
return v.UserID
case *tg.PeerChat:
return v.ChatID
case *tg.PeerChannel:
return v.ChannelID
default:
panic(fmt.Errorf("unknown peer class type %T", v))
}
}
func InternalMakePortalKey(peer tg.PeerClass, receiver networkid.UserLoginID) networkid.PortalKey {
switch v := peer.(type) {
case *tg.PeerUser:
return networkid.PortalKey{
ID: networkid.PortalID(fmt.Sprintf("%s:%d", PeerTypeUser, v.UserID)),
Receiver: receiver,
}
case *tg.PeerChat:
return networkid.PortalKey{
ID: networkid.PortalID(fmt.Sprintf("%s:%d", PeerTypeChat, v.ChatID)),
Receiver: receiver,
}
case *tg.PeerChannel:
return networkid.PortalKey{ID: networkid.PortalID(fmt.Sprintf("%s:%d", PeerTypeChannel, v.ChannelID))}
default:
panic(fmt.Errorf("unknown peer class type %T", v))
}
}
func ParsePortalID(portalID networkid.PortalID) (pt PeerType, id int64, err error) {
parts := strings.Split(string(portalID), ":")
pt = PeerType(parts[0])
id, err = strconv.ParseInt(parts[1], 10, 64)
return
}
func MakeAvatarID(photoID int64) networkid.AvatarID {
return networkid.AvatarID(strconv.FormatInt(photoID, 10))
}
func MakeEmojiIDFromDocumentID(documentID int64) networkid.EmojiID {
return networkid.EmojiID(strconv.FormatInt(documentID, 10))
}
func MakeEmojiIDFromEmoticon(emoji string) networkid.EmojiID {
return networkid.EmojiID(emoji)
}
func isNumbers(s string) bool {
for _, r := range s {
if r < '0' || r > '9' {
return false
}
}
return true
}
func ParseEmojiID(emojiID networkid.EmojiID) (documentID int64, emoji string, err error) {
if isNumbers(string(emojiID)) {
documentID, err = strconv.ParseInt(string(emojiID), 10, 64)
} else {
emoji = string(emojiID)
}
return
}