Add support for app ads

This commit is contained in:
Ilya Laktyushin
2023-11-17 03:13:55 +04:00
parent f66b521e2f
commit e8e2d419c5
8 changed files with 106 additions and 35 deletions

View File

@@ -10497,3 +10497,8 @@ Sorry for the inconvenience.";
"Stats.StoryReactionsByEmotionTitle" = "STORIES REACTIONS BY EMOTION";
"Stats.MessageReactionsTitle" = "REACTIONS";
"Conversation.LaunchApp" = "LAUNCH APP";
"Message.AdSponsoredLabel" = "Sponsored";
"Message.AdRecommendedLabel" = "Recommended";

View File

@@ -797,7 +797,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-651419003] = { return Api.SendMessageAction.parse_speakingInGroupCallAction($0) }
dict[-1239335713] = { return Api.ShippingOption.parse_shippingOption($0) }
dict[-2010155333] = { return Api.SimpleWebViewResult.parse_simpleWebViewResultUrl($0) }
dict[-626000021] = { return Api.SponsoredMessage.parse_sponsoredMessage($0) }
dict[-313293833] = { return Api.SponsoredMessage.parse_sponsoredMessage($0) }
dict[1035529315] = { return Api.SponsoredWebPage.parse_sponsoredWebPage($0) }
dict[-884757282] = { return Api.StatsAbsValueAndPrev.parse_statsAbsValueAndPrev($0) }
dict[-1237848657] = { return Api.StatsDateRangeDays.parse_statsDateRangeDays($0) }

View File

@@ -434,13 +434,13 @@ public extension Api {
}
public extension Api {
indirect enum SponsoredMessage: TypeConstructorDescription {
case sponsoredMessage(flags: Int32, randomId: Buffer, fromId: Api.Peer?, chatInvite: Api.ChatInvite?, chatInviteHash: String?, channelPost: Int32?, startParam: String?, webpage: Api.SponsoredWebPage?, message: String, entities: [Api.MessageEntity]?, sponsorInfo: String?, additionalInfo: String?)
case sponsoredMessage(flags: Int32, randomId: Buffer, fromId: Api.Peer?, chatInvite: Api.ChatInvite?, chatInviteHash: String?, channelPost: Int32?, startParam: String?, webpage: Api.SponsoredWebPage?, app: Api.BotApp?, message: String, entities: [Api.MessageEntity]?, buttonText: String?, sponsorInfo: String?, additionalInfo: String?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .sponsoredMessage(let flags, let randomId, let fromId, let chatInvite, let chatInviteHash, let channelPost, let startParam, let webpage, let message, let entities, let sponsorInfo, let additionalInfo):
case .sponsoredMessage(let flags, let randomId, let fromId, let chatInvite, let chatInviteHash, let channelPost, let startParam, let webpage, let app, let message, let entities, let buttonText, let sponsorInfo, let additionalInfo):
if boxed {
buffer.appendInt32(-626000021)
buffer.appendInt32(-313293833)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeBytes(randomId, buffer: buffer, boxed: false)
@@ -450,12 +450,14 @@ public extension Api {
if Int(flags) & Int(1 << 2) != 0 {serializeInt32(channelPost!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 0) != 0 {serializeString(startParam!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 9) != 0 {webpage!.serialize(buffer, true)}
if Int(flags) & Int(1 << 10) != 0 {app!.serialize(buffer, true)}
serializeString(message, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261)
buffer.appendInt32(Int32(entities!.count))
for item in entities! {
item.serialize(buffer, true)
}}
if Int(flags) & Int(1 << 11) != 0 {serializeString(buttonText!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 7) != 0 {serializeString(sponsorInfo!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 8) != 0 {serializeString(additionalInfo!, buffer: buffer, boxed: false)}
break
@@ -464,8 +466,8 @@ public extension Api {
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .sponsoredMessage(let flags, let randomId, let fromId, let chatInvite, let chatInviteHash, let channelPost, let startParam, let webpage, let message, let entities, let sponsorInfo, let additionalInfo):
return ("sponsoredMessage", [("flags", flags as Any), ("randomId", randomId as Any), ("fromId", fromId as Any), ("chatInvite", chatInvite as Any), ("chatInviteHash", chatInviteHash as Any), ("channelPost", channelPost as Any), ("startParam", startParam as Any), ("webpage", webpage as Any), ("message", message as Any), ("entities", entities as Any), ("sponsorInfo", sponsorInfo as Any), ("additionalInfo", additionalInfo as Any)])
case .sponsoredMessage(let flags, let randomId, let fromId, let chatInvite, let chatInviteHash, let channelPost, let startParam, let webpage, let app, let message, let entities, let buttonText, let sponsorInfo, let additionalInfo):
return ("sponsoredMessage", [("flags", flags as Any), ("randomId", randomId as Any), ("fromId", fromId as Any), ("chatInvite", chatInvite as Any), ("chatInviteHash", chatInviteHash as Any), ("channelPost", channelPost as Any), ("startParam", startParam as Any), ("webpage", webpage as Any), ("app", app as Any), ("message", message as Any), ("entities", entities as Any), ("buttonText", buttonText as Any), ("sponsorInfo", sponsorInfo as Any), ("additionalInfo", additionalInfo as Any)])
}
}
@@ -492,16 +494,22 @@ public extension Api {
if Int(_1!) & Int(1 << 9) != 0 {if let signature = reader.readInt32() {
_8 = Api.parse(reader, signature: signature) as? Api.SponsoredWebPage
} }
var _9: String?
_9 = parseString(reader)
var _10: [Api.MessageEntity]?
if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() {
_10 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self)
var _9: Api.BotApp?
if Int(_1!) & Int(1 << 10) != 0 {if let signature = reader.readInt32() {
_9 = Api.parse(reader, signature: signature) as? Api.BotApp
} }
var _10: String?
_10 = parseString(reader)
var _11: [Api.MessageEntity]?
if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() {
_11 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self)
} }
var _11: String?
if Int(_1!) & Int(1 << 7) != 0 {_11 = parseString(reader) }
var _12: String?
if Int(_1!) & Int(1 << 8) != 0 {_12 = parseString(reader) }
if Int(_1!) & Int(1 << 11) != 0 {_12 = parseString(reader) }
var _13: String?
if Int(_1!) & Int(1 << 7) != 0 {_13 = parseString(reader) }
var _14: String?
if Int(_1!) & Int(1 << 8) != 0 {_14 = parseString(reader) }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = (Int(_1!) & Int(1 << 3) == 0) || _3 != nil
@@ -510,12 +518,14 @@ public extension Api {
let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil
let _c7 = (Int(_1!) & Int(1 << 0) == 0) || _7 != nil
let _c8 = (Int(_1!) & Int(1 << 9) == 0) || _8 != nil
let _c9 = _9 != nil
let _c10 = (Int(_1!) & Int(1 << 1) == 0) || _10 != nil
let _c11 = (Int(_1!) & Int(1 << 7) == 0) || _11 != nil
let _c12 = (Int(_1!) & Int(1 << 8) == 0) || _12 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 {
return Api.SponsoredMessage.sponsoredMessage(flags: _1!, randomId: _2!, fromId: _3, chatInvite: _4, chatInviteHash: _5, channelPost: _6, startParam: _7, webpage: _8, message: _9!, entities: _10, sponsorInfo: _11, additionalInfo: _12)
let _c9 = (Int(_1!) & Int(1 << 10) == 0) || _9 != nil
let _c10 = _10 != nil
let _c11 = (Int(_1!) & Int(1 << 1) == 0) || _11 != nil
let _c12 = (Int(_1!) & Int(1 << 11) == 0) || _12 != nil
let _c13 = (Int(_1!) & Int(1 << 7) == 0) || _13 != nil
let _c14 = (Int(_1!) & Int(1 << 8) == 0) || _14 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 {
return Api.SponsoredMessage.sponsoredMessage(flags: _1!, randomId: _2!, fromId: _3, chatInvite: _4, chatInviteHash: _5, channelPost: _6, startParam: _7, webpage: _8, app: _9, message: _10!, entities: _11, buttonText: _12, sponsorInfo: _13, additionalInfo: _14)
}
else {
return nil

View File

@@ -11,20 +11,23 @@ public final class AdMessageAttribute: MessageAttribute {
case peer(id: EnginePeer.Id, message: EngineMessage.Id?, startParam: String?)
case join(title: String, joinHash: String)
case webPage(title: String, url: String)
case botApp(peerId: EnginePeer.Id, app: BotApp, startParam: String?)
}
public let opaqueId: Data
public let messageType: MessageType
public let displayAvatar: Bool
public let target: MessageTarget
public let buttonText: String?
public let sponsorInfo: String?
public let additionalInfo: String?
public init(opaqueId: Data, messageType: MessageType, displayAvatar: Bool, target: MessageTarget, sponsorInfo: String?, additionalInfo: String?) {
public init(opaqueId: Data, messageType: MessageType, displayAvatar: Bool, target: MessageTarget, buttonText: String?, sponsorInfo: String?, additionalInfo: String?) {
self.opaqueId = opaqueId
self.messageType = messageType
self.displayAvatar = displayAvatar
self.target = target
self.buttonText = buttonText
self.sponsorInfo = sponsorInfo
self.additionalInfo = additionalInfo
}

View File

@@ -15,6 +15,7 @@ private class AdMessagesHistoryContextImpl {
case target
case messageId
case startParam
case buttonText
case sponsorInfo
case additionalInfo
}
@@ -33,6 +34,7 @@ private class AdMessagesHistoryContextImpl {
case peer
case invite
case webPage
case botApp
}
struct Invite: Equatable, Codable {
@@ -78,11 +80,14 @@ private class AdMessagesHistoryContextImpl {
case peer(PeerId)
case invite(Invite)
case webPage(WebPage)
case botApp(PeerId, BotApp)
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
if let peer = try container.decodeIfPresent(Int64.self, forKey: .peer) {
if let botApp = try container.decodeIfPresent(BotApp.self, forKey: .botApp), let peer = try container.decodeIfPresent(Int64.self, forKey: .peer) {
self = .botApp(PeerId(peer), botApp)
} else if let peer = try container.decodeIfPresent(Int64.self, forKey: .peer) {
self = .peer(PeerId(peer))
} else if let invite = try container.decodeIfPresent(Invite.self, forKey: .invite) {
self = .invite(invite)
@@ -103,6 +108,9 @@ private class AdMessagesHistoryContextImpl {
try container.encode(invite, forKey: .invite)
case let .webPage(webPage):
try container.encode(webPage, forKey: .webPage)
case let .botApp(peerId, botApp):
try container.encode(peerId.toInt64(), forKey: .peer)
try container.encode(botApp, forKey: .botApp)
}
}
}
@@ -116,6 +124,7 @@ private class AdMessagesHistoryContextImpl {
public let target: Target
public let messageId: MessageId?
public let startParam: String?
public let buttonText: String?
public let sponsorInfo: String?
public let additionalInfo: String?
@@ -129,6 +138,7 @@ private class AdMessagesHistoryContextImpl {
target: Target,
messageId: MessageId?,
startParam: String?,
buttonText: String?,
sponsorInfo: String?,
additionalInfo: String?
) {
@@ -141,6 +151,7 @@ private class AdMessagesHistoryContextImpl {
self.target = target
self.messageId = messageId
self.startParam = startParam
self.buttonText = buttonText
self.sponsorInfo = sponsorInfo
self.additionalInfo = additionalInfo
}
@@ -169,6 +180,7 @@ private class AdMessagesHistoryContextImpl {
self.target = try container.decode(Target.self, forKey: .target)
self.messageId = try container.decodeIfPresent(MessageId.self, forKey: .messageId)
self.startParam = try container.decodeIfPresent(String.self, forKey: .startParam)
self.buttonText = try container.decodeIfPresent(String.self, forKey: .buttonText)
self.sponsorInfo = try container.decodeIfPresent(String.self, forKey: .sponsorInfo)
self.additionalInfo = try container.decodeIfPresent(String.self, forKey: .additionalInfo)
@@ -193,6 +205,7 @@ private class AdMessagesHistoryContextImpl {
try container.encode(self.target, forKey: .target)
try container.encodeIfPresent(self.messageId, forKey: .messageId)
try container.encodeIfPresent(self.startParam, forKey: .startParam)
try container.encodeIfPresent(self.buttonText, forKey: .buttonText)
try container.encodeIfPresent(self.sponsorInfo, forKey: .sponsorInfo)
try container.encodeIfPresent(self.additionalInfo, forKey: .additionalInfo)
@@ -228,6 +241,9 @@ private class AdMessagesHistoryContextImpl {
if lhs.startParam != rhs.startParam {
return false
}
if lhs.buttonText != rhs.buttonText {
return false
}
if lhs.sponsorInfo != rhs.sponsorInfo {
return false
}
@@ -248,6 +264,8 @@ private class AdMessagesHistoryContextImpl {
target = .join(title: invite.title, joinHash: invite.joinHash)
case let .webPage(webPage):
target = .webPage(title: webPage.title, url: webPage.url)
case let .botApp(peerId, botApp):
target = .botApp(peerId: peerId, app: botApp, startParam: self.startParam)
}
let mappedMessageType: AdMessageAttribute.MessageType
switch self.messageType {
@@ -256,7 +274,7 @@ private class AdMessagesHistoryContextImpl {
case .recommended:
mappedMessageType = .recommended
}
attributes.append(AdMessageAttribute(opaqueId: self.opaqueId, messageType: mappedMessageType, displayAvatar: self.displayAvatar, target: target, sponsorInfo: self.sponsorInfo, additionalInfo: self.additionalInfo))
attributes.append(AdMessageAttribute(opaqueId: self.opaqueId, messageType: mappedMessageType, displayAvatar: self.displayAvatar, target: target, buttonText: self.buttonText, sponsorInfo: self.sponsorInfo, additionalInfo: self.additionalInfo))
if !self.textEntities.isEmpty {
let attribute = TextEntitiesMessageAttribute(entities: self.textEntities)
attributes.append(attribute)
@@ -270,7 +288,7 @@ private class AdMessagesHistoryContextImpl {
let author: Peer
switch self.target {
case let .peer(peerId):
case let .peer(peerId), let .botApp(peerId, _):
if let peer = transaction.getPeer(peerId) {
author = peer
} else {
@@ -523,7 +541,7 @@ private class AdMessagesHistoryContextImpl {
for message in messages {
switch message {
case let .sponsoredMessage(flags, randomId, fromId, chatInvite, chatInviteHash, channelPost, startParam, webPage, message, entities, sponsorInfo, additionalInfo):
case let .sponsoredMessage(flags, randomId, fromId, chatInvite, chatInviteHash, channelPost, startParam, webPage, botApp, message, entities, buttonText, sponsorInfo, additionalInfo):
var parsedEntities: [MessageTextEntity] = []
if let entities = entities {
parsedEntities = messageTextEntitiesFromApiEntities(entities)
@@ -534,7 +552,11 @@ private class AdMessagesHistoryContextImpl {
var target: CachedMessage.Target?
if let fromId = fromId {
if let botApp = botApp, let app = BotApp(apiBotApp: botApp) {
target = .botApp(fromId.peerId, app)
} else {
target = .peer(fromId.peerId)
}
} else if let webPage = webPage {
switch webPage {
case let .sponsoredWebPage(_, url, siteName, photo):
@@ -575,6 +597,9 @@ private class AdMessagesHistoryContextImpl {
}
}
}
// else if let botApp = app.flatMap({ BotApp(apiBotApp: $0) }) {
// target = .botApp(botApp)
// }
var messageId: MessageId?
if let fromId = fromId, let channelPost = channelPost {
@@ -592,6 +617,7 @@ private class AdMessagesHistoryContextImpl {
target: target,
messageId: messageId,
startParam: startParam,
buttonText: buttonText,
sponsorInfo: sponsorInfo,
additionalInfo: additionalInfo
))

View File

@@ -776,3 +776,14 @@ func _internal_getBotApp(account: Account, reference: BotAppReference) -> Signal
|> castError(GetBotAppError.self)
|> switchToLatest
}
extension BotApp {
convenience init?(apiBotApp: Api.BotApp) {
switch apiBotApp {
case let .botApp(_, id, accessHash, shortName, title, description, photo, document, hash):
self.init(id: id, accessHash: accessHash, shortName: shortName, title: title, description: description, photo: telegramMediaImageFromApiPhoto(photo), document: document.flatMap(telegramMediaFileFromApiDocument), hash: hash, flags: [])
case .botAppNotModified:
return nil
}
}
}

View File

@@ -479,9 +479,12 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent
}
}
} else if let adAttribute = item.message.adAttribute {
//TODO:localize
//Recommended?
title = "Sponsored"
switch adAttribute.messageType {
case .sponsored:
title = item.presentationData.strings.Message_AdSponsoredLabel
case .recommended:
title = item.presentationData.strings.Message_AdRecommendedLabel
}
subtitle = item.message.author.flatMap {
NSAttributedString(string: EnginePeer($0).compactDisplayTitle, font: titleFont)
}
@@ -500,8 +503,14 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent
}
}
if let author = item.message.author as? TelegramUser, author.botInfo != nil {
if let buttonText = adAttribute.buttonText {
actionTitle = buttonText.uppercased()
} else if let author = item.message.author as? TelegramUser, author.botInfo != nil {
if case .botApp = adAttribute.target {
actionTitle = item.presentationData.strings.Conversation_LaunchApp
} else {
actionTitle = item.presentationData.strings.Conversation_ViewBot
}
} else if let author = item.message.author as? TelegramChannel, case .group = author.info {
if case let .peer(_, messageId, _) = adAttribute.target, messageId != nil {
actionTitle = item.presentationData.strings.Conversation_ViewPost

View File

@@ -4102,6 +4102,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.controllerInteraction?.openJoinLink(joinHash)
case let .webPage(_, url):
self.controllerInteraction?.openUrl(ChatControllerInteraction.OpenUrl(url: url, concealed: false, external: false))
case let .botApp(peerId, botApp, startParam):
let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|> deliverOnMainQueue).startStandalone(next: { [weak self] peer in
if let self, let peer {
self.presentBotApp(botApp: botApp, botPeer: peer, payload: startParam)
}
})
}
}, openRequestedPeerSelection: { [weak self] messageId, peerType, buttonId in
guard let self else {