From 589168318a24482a3504f62468ab5aee52affbfd Mon Sep 17 00:00:00 2001 From: Mikhail Filimonov Date: Fri, 16 Jan 2026 17:52:58 +0400 Subject: [PATCH 1/4] update api (rarities, crafted) --- .../ApiUtils/TelegramMediaAction.swift | 2 +- .../SyncCore_TelegramMediaAction.swift | 17 ++++++++----- .../TelegramEngine/Payments/StarGifts.swift | 25 +++++++++++++++++++ 3 files changed, 37 insertions(+), 7 deletions(-) diff --git a/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaAction.swift b/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaAction.swift index fea49560a1..4a2a4cf19b 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaAction.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaAction.swift @@ -205,7 +205,7 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe text = nil entities = nil } - return TelegramMediaAction(action: .giftCode(slug: slug, fromGiveaway: (flags & (1 << 0)) != 0, isUnclaimed: (flags & (1 << 5)) != 0, boostPeerId: boostPeer?.peerId, days: days, currency: currency, amount: amount, cryptoCurrency: cryptoCurrency, cryptoAmount: cryptoAmount, text: text, entities: entities)) + return TelegramMediaAction(action: .giftCode(slug: slug, fromGiveaway: (flags & (1 << 0)) != 0, isUnclaimed: (flags & (1 << 5)) != 0, boostPeerId: boostPeer?.peerId, months: days, currency: currency, amount: amount, cryptoCurrency: cryptoCurrency, cryptoAmount: cryptoAmount, text: text, entities: entities)) case let .messageActionGiveawayLaunch(messageActionGiveawayLaunchData): return TelegramMediaAction(action: .giveawayLaunched(stars: messageActionGiveawayLaunchData.stars)) case let .messageActionGiveawayResults(messageActionGiveawayResultsData): diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift index 850a25fb5a..854b8e3caa 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift @@ -247,7 +247,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { case requestedPeer(buttonId: Int32, peerIds: [PeerId]) case setChatWallpaper(wallpaper: TelegramWallpaper, forBoth: Bool) case setSameChatWallpaper(wallpaper: TelegramWallpaper) - case giftCode(slug: String, fromGiveaway: Bool, isUnclaimed: Bool, boostPeerId: PeerId?, days: Int32, currency: String?, amount: Int64?, cryptoCurrency: String?, cryptoAmount: Int64?, text: String?, entities: [MessageTextEntity]?) + case giftCode(slug: String, fromGiveaway: Bool, isUnclaimed: Bool, boostPeerId: PeerId?, months: Int32, currency: String?, amount: Int64?, cryptoCurrency: String?, cryptoAmount: Int64?, text: String?, entities: [MessageTextEntity]?) case giveawayLaunched(stars: Int64?) case joinedChannel case giveawayResults(winners: Int32, unclaimed: Int32, stars: Bool) @@ -269,6 +269,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { case suggestedBirthday(TelegramBirthday) case starGiftPurchaseOffer(gift: StarGift, amount: CurrencyAmount, expireDate: Int32, isAccepted: Bool, isDeclined: Bool) case starGiftPurchaseOfferDeclined(gift: StarGift, amount: CurrencyAmount, hasExpired: Bool) + case starGiftCraftFail public init(decoder: PostboxDecoder) { let rawValue: Int32 = decoder.decodeInt32ForKey("_rawValue", orElse: 0) @@ -374,7 +375,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { case 35: self = .botAppAccessGranted(appName: decoder.decodeOptionalStringForKey("app"), type: decoder.decodeOptionalInt32ForKey("atp").flatMap { BotSendMessageAccessGrantedType(rawValue: $0) }) case 36: - self = .giftCode(slug: decoder.decodeStringForKey("slug", orElse: ""), fromGiveaway: decoder.decodeBoolForKey("give", orElse: false), isUnclaimed: decoder.decodeBoolForKey("unclaimed", orElse: false), boostPeerId: decoder.decodeOptionalInt64ForKey("pi").flatMap { PeerId($0) }, days: decoder.decodeInt32ForKey("days", orElse: decoder.decodeInt32ForKey("months", orElse: 0)), currency: decoder.decodeOptionalStringForKey("currency"), amount: decoder.decodeOptionalInt64ForKey("amount"), cryptoCurrency: decoder.decodeOptionalStringForKey("cryptoCurrency"), cryptoAmount: decoder.decodeOptionalInt64ForKey("cryptoAmount"), text: decoder.decodeOptionalStringForKey("text"), entities: decoder.decodeOptionalObjectArrayWithDecoderForKey("entities")) + self = .giftCode(slug: decoder.decodeStringForKey("slug", orElse: ""), fromGiveaway: decoder.decodeBoolForKey("give", orElse: false), isUnclaimed: decoder.decodeBoolForKey("unclaimed", orElse: false), boostPeerId: decoder.decodeOptionalInt64ForKey("pi").flatMap { PeerId($0) }, months: decoder.decodeInt32ForKey("months", orElse: 0), currency: decoder.decodeOptionalStringForKey("currency"), amount: decoder.decodeOptionalInt64ForKey("amount"), cryptoCurrency: decoder.decodeOptionalStringForKey("cryptoCurrency"), cryptoAmount: decoder.decodeOptionalInt64ForKey("cryptoAmount"), text: decoder.decodeOptionalStringForKey("text"), entities: decoder.decodeOptionalObjectArrayWithDecoderForKey("entities")) case 37: self = .giveawayLaunched(stars: decoder.decodeOptionalInt64ForKey("stars")) case 38: @@ -437,9 +438,11 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { case 55: self = .suggestedBirthday(decoder.decodeCodable(TelegramBirthday.self, forKey: "birthday") ?? TelegramBirthday(day: 1, month: 1, year: nil)) case 56: - self = .starGiftPurchaseOffer(gift: decoder.decodeObjectForKey("gift", decoder: { StarGift(decoder: $0) }) as! StarGift, amount: decoder.decodeCodable(CurrencyAmount.self, forKey: "amount")!, expireDate: decoder.decodeInt32ForKey("expireDate", orElse: 0), isAccepted: decoder.decodeBoolForKey("isAccepted", orElse: false), isDeclined: decoder.decodeBoolForKey("isDeclined", orElse: false)) + self = .starGiftPurchaseOffer(gift: decoder.decodeObjectForKey("gift", decoder: { StarGift(decoder: $0) }) as! StarGift, amount: decoder.decodeCodable(CurrencyAmount.self, forKey: "amount") ?? CurrencyAmount(amount: .zero, currency: .stars), expireDate: decoder.decodeInt32ForKey("expireDate", orElse: 0), isAccepted: decoder.decodeBoolForKey("isAccepted", orElse: false), isDeclined: decoder.decodeBoolForKey("isDeclined", orElse: false)) case 57: - self = .starGiftPurchaseOfferDeclined(gift: decoder.decodeObjectForKey("gift", decoder: { StarGift(decoder: $0) }) as! StarGift, amount: decoder.decodeCodable(CurrencyAmount.self, forKey: "amount")!, hasExpired: decoder.decodeBoolForKey("hasExpired", orElse: false)) + self = .starGiftPurchaseOfferDeclined(gift: decoder.decodeObjectForKey("gift", decoder: { StarGift(decoder: $0) }) as! StarGift, amount: decoder.decodeCodable(CurrencyAmount.self, forKey: "amount") ?? CurrencyAmount(amount: .zero, currency: .stars), hasExpired: decoder.decodeBoolForKey("hasExpired", orElse: false)) + case 58: + self = .starGiftCraftFail default: self = .unknown } @@ -632,7 +635,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { } else { encoder.encodeNil(forKey: "atp") } - case let .giftCode(slug, fromGiveaway, unclaimed, boostPeerId, days, currency, amount, cryptoCurrency, cryptoAmount, text, entities): + case let .giftCode(slug, fromGiveaway, unclaimed, boostPeerId, months, currency, amount, cryptoCurrency, cryptoAmount, text, entities): encoder.encodeInt32(36, forKey: "_rawValue") encoder.encodeString(slug, forKey: "slug") encoder.encodeBool(fromGiveaway, forKey: "give") @@ -642,7 +645,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { } else { encoder.encodeNil(forKey: "pi") } - encoder.encodeInt32(days, forKey: "days") + encoder.encodeInt32(months, forKey: "months") if let currency = currency { encoder.encodeString(currency, forKey: "currency") } else { @@ -929,6 +932,8 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { encoder.encodeObject(gift, forKey: "gift") encoder.encodeCodable(amount, forKey: "amount") encoder.encodeBool(hasExpired, forKey: "hasExpired") + case .starGiftCraftFail: + encoder.encodeInt32(58, forKey: "_rawValue") } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift index 8fc803a7db..458361aeb4 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift @@ -469,6 +469,31 @@ public enum StarGift: Equatable, Codable, PostboxCoding { return 0 } } + + public var isPermille: Bool { + if case .permille = self { + return true + } + return false + } + + public var badgeText: String { + switch self { + case let .permille(value): + let percent = Double(value) / 10.0 + if percent.truncatingRemainder(dividingBy: 1) == 0 { + return "\(Int(percent))%" + } else { + return String(format: "%.1f%%", percent) + } + case .rare: + return "Rare" + case .epic: + return "Epic" + case .legendary: + return "Legendary" + } + } } case model(name: String, file: TelegramMediaFile, rarity: Rarity, crafted: Bool) From c1659b34e8ec7a0798771dd6143792bd4e0d5e60 Mon Sep 17 00:00:00 2001 From: Mikhail Filimonov Date: Tue, 27 Jan 2026 10:12:34 +0100 Subject: [PATCH 2/4] macos related --- .../TelegramEngine/Messages/RequestMessageActionCallback.swift | 2 +- .../Sources/TelegramEngine/Payments/StarGifts.swift | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/RequestMessageActionCallback.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/RequestMessageActionCallback.swift index a07520bb90..bf017308e3 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/RequestMessageActionCallback.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/RequestMessageActionCallback.swift @@ -180,7 +180,7 @@ public enum MessageActionUrlAuthResult { public static let requestPhoneNumber = Flags(rawValue: 1 << 1) } - public struct ClientData { + public struct ClientData : Equatable { public let browser: String public let platform: String public let ip: String diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift index 458361aeb4..19d4d018d5 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift @@ -480,6 +480,9 @@ public enum StarGift: Equatable, Codable, PostboxCoding { public var badgeText: String { switch self { case let .permille(value): + if value == 0 { + return "<0.1%" + } let percent = Double(value) / 10.0 if percent.truncatingRemainder(dividingBy: 1) == 0 { return "\(Int(percent))%" From 4d99e3ad01282a4b0c51522e3327a913480901bb Mon Sep 17 00:00:00 2001 From: Mikhail Filimonov Date: Fri, 30 Jan 2026 08:21:55 +0100 Subject: [PATCH 3/4] badge --- .../Sources/TelegramEngine/Payments/StarGifts.swift | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift index cc6a6ce40c..b38c96a9c6 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift @@ -491,11 +491,13 @@ public enum StarGift: Equatable, Codable, PostboxCoding { return String(format: "%.1f%%", percent) } case .rare: - return "Rare" + return "rare" case .epic: - return "Epic" + return "epic" case .legendary: - return "Legendary" + return "legendary" + case .uncommon: + return "uncommon" } } } From 15d0267e72d6d866751e2ab0c9d56cb3afabbe4d Mon Sep 17 00:00:00 2001 From: Mikhail Filimonov Date: Fri, 30 Jan 2026 12:09:19 +0100 Subject: [PATCH 4/4] burned flag --- .../TelegramEngine/Payments/StarGifts.swift | 46 +++++++++++-------- .../Payments/TelegramEnginePayments.swift | 2 +- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift index 81359793df..9dac20fba5 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift @@ -426,6 +426,7 @@ public enum StarGift: Equatable, Codable, PostboxCoding { } public static let isThemeAvailable = Flags(rawValue: 1 << 0) + public static let isBurned = Flags(rawValue: 1 << 1) } public enum Attribute: Equatable, Codable, PostboxCoding { @@ -1308,13 +1309,16 @@ extension StarGift { } else if let ownerName { owner = .name(ownerName) } else { - owner = nil + owner = .none } let resellAmounts = apiResellAmount?.compactMap { CurrencyAmount(apiAmount: $0) } var flags = StarGift.UniqueGift.Flags() if (apiFlags & (1 << 9)) != 0 { flags.insert(.isThemeAvailable) } + if (apiFlags & (1 << 14)) != 0 { + flags.insert(.isBurned) + } var peerCollectibleColor: PeerCollectibleColor? switch peerColor { case let .peerColorCollectible(peerColorCollectibleData): @@ -3589,28 +3593,32 @@ extension StarGift.UniqueGift.Attribute { } -func _internal_getUniqueStarGift(account: Account, slug: String) -> Signal { +public enum GetUniqueStarGiftError { + case generic + case burned +} + +func _internal_getUniqueStarGift(account: Account, slug: String) -> Signal { return account.network.request(Api.functions.payments.getUniqueStarGift(slug: slug)) - |> map(Optional.init) - |> `catch` { _ -> Signal in - return .single(nil) + |> mapError { error -> GetUniqueStarGiftError in + if error.errorDescription == "STARGIFT_ALREADY_BURNED" { + return .burned + } + return .generic } - |> mapToSignal { result -> Signal in - if let result = result { - switch result { - case let .uniqueStarGift(uniqueStarGiftData): - let (gift, chats, users) = (uniqueStarGiftData.gift, uniqueStarGiftData.chats, uniqueStarGiftData.users) - return account.postbox.transaction { transaction in - let parsedPeers = AccumulatedPeers(chats: chats, users: users) - updatePeers(transaction: transaction, accountPeerId: account.peerId, peers: parsedPeers) - guard case let .unique(uniqueGift) = StarGift(apiStarGift: gift) else { - return nil - } - return uniqueGift + |> mapToSignal { result -> Signal in + switch result { + case let .uniqueStarGift(uniqueStarGiftData): + let (gift, chats, users) = (uniqueStarGiftData.gift, uniqueStarGiftData.chats, uniqueStarGiftData.users) + return account.postbox.transaction { transaction in + let parsedPeers = AccumulatedPeers(chats: chats, users: users) + updatePeers(transaction: transaction, accountPeerId: account.peerId, peers: parsedPeers) + guard case let .unique(uniqueGift) = StarGift(apiStarGift: gift) else { + return nil } + return uniqueGift } - } else { - return .single(nil) + |> castError(GetUniqueStarGiftError.self) } } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/TelegramEnginePayments.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/TelegramEnginePayments.swift index ec9b54d7c2..ff50d3caf4 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/TelegramEnginePayments.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/TelegramEnginePayments.swift @@ -149,7 +149,7 @@ public extension TelegramEngine { return _internal_checkCanSendStarGift(account: self.account, giftId: giftId) } - public func getUniqueStarGift(slug: String) -> Signal { + public func getUniqueStarGift(slug: String) -> Signal { return _internal_getUniqueStarGift(account: self.account, slug: slug) }