This commit is contained in:
Ali 2020-11-13 20:55:41 +04:00
parent edbbc48340
commit 0e3445983c
22 changed files with 2020 additions and 67 deletions

View File

@ -3,7 +3,7 @@
@implementation Serialization
- (NSUInteger)currentLayer {
return 120;
return 122;
}
- (id _Nullable)parseMessage:(NSData * _Nullable)data {

View File

@ -151,8 +151,13 @@ public protocol PresentationCall: class {
func makeOutgoingVideoView(completion: @escaping (PresentationCallVideoView?) -> Void)
}
public protocol PresentationGroupCall: class {
}
public protocol PresentationCallManager: class {
var currentCallSignal: Signal<PresentationCall?, NoError> { get }
func requestCall(context: AccountContext, peerId: PeerId, isVideo: Bool, endCurrentIfAny: Bool) -> RequestCallResult
func requestOrJoinGroupCall(context: AccountContext, peerId: PeerId)
}

View File

@ -215,7 +215,7 @@ public func mergeListsStableWithUpdates<T>(leftList: [T], rightList: [T], isLess
for item in rightList {
rightStableIds.append(getId(item))
}
if Set(leftStableIds) == Set(rightStableIds) && leftStableIds != rightStableIds && !allUpdated {
if false && Set(leftStableIds) == Set(rightStableIds) && leftStableIds != rightStableIds && !allUpdated {
var updatedItems: [(T, AnyHashable)] = []
for i in 0 ..< leftList.count {
if getId(leftList[i]) != getId(rightList[i]) {

View File

@ -46,6 +46,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
case peerJoined
case phoneNumberRequest
case geoProximityReached(from: PeerId, to: PeerId, distance: Int32)
case groupPhoneCall(callId: Int64, accessHash: Int64, duration: Int32?)
public init(decoder: PostboxDecoder) {
let rawValue: Int32 = decoder.decodeInt32ForKey("_rawValue", orElse: 0)
@ -98,6 +99,8 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
self = .phoneNumberRequest
case 21:
self = .geoProximityReached(from: PeerId(decoder.decodeInt64ForKey("fromId", orElse: 0)), to: PeerId(decoder.decodeInt64ForKey("toId", orElse: 0)), distance: (decoder.decodeInt32ForKey("dst", orElse: 0)))
case 22:
self = .groupPhoneCall(callId: decoder.decodeInt64ForKey("callId", orElse: 0), accessHash: decoder.decodeInt64ForKey("accessHash", orElse: 0), duration: decoder.decodeOptionalInt32ForKey("duration"))
default:
self = .unknown
}
@ -188,6 +191,15 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
encoder.encodeInt64(from.toInt64(), forKey: "fromId")
encoder.encodeInt64(to.toInt64(), forKey: "toId")
encoder.encodeInt32(distance, forKey: "dst")
case let .groupPhoneCall(callId, accessHash, duration):
encoder.encodeInt32(22, forKey: "_rawValue")
encoder.encodeInt64(callId, forKey: "callId")
encoder.encodeInt64(accessHash, forKey: "accessHash")
if let duration = duration {
encoder.encodeInt32(duration, forKey: "duration")
} else {
encoder.encodeNil(forKey: "duration")
}
}
}

View File

@ -6,11 +6,14 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[571523412] = { return $0.readDouble() }
dict[-1255641564] = { return parseString($0) }
dict[-1240849242] = { return Api.messages.StickerSet.parse_stickerSet($0) }
dict[1829443076] = { return Api.GroupCall.parse_groupCallPrivate($0) }
dict[-857633264] = { return Api.GroupCall.parse_groupCall($0) }
dict[2004925620] = { return Api.GroupCall.parse_groupCallDiscarded($0) }
dict[-457104426] = { return Api.InputGeoPoint.parse_inputGeoPointEmpty($0) }
dict[1210199983] = { return Api.InputGeoPoint.parse_inputGeoPoint($0) }
dict[-784000893] = { return Api.payments.ValidatedRequestedInfo.parse_validatedRequestedInfo($0) }
dict[461151667] = { return Api.ChatFull.parse_chatFull($0) }
dict[-253335766] = { return Api.ChatFull.parse_channelFull($0) }
dict[-428758403] = { return Api.ChatFull.parse_channelFull($0) }
dict[-1159937629] = { return Api.PollResults.parse_pollResults($0) }
dict[-925415106] = { return Api.ChatParticipant.parse_chatParticipant($0) }
dict[-636267638] = { return Api.ChatParticipant.parse_chatParticipantCreator($0) }
@ -132,6 +135,11 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[1511503333] = { return Api.InputEncryptedFile.parse_inputEncryptedFile($0) }
dict[767652808] = { return Api.InputEncryptedFile.parse_inputEncryptedFileBigUploaded($0) }
dict[-1456996667] = { return Api.messages.InactiveChats.parse_inactiveChats($0) }
dict[-1513019911] = { return Api.GroupCallParticipant.parse_groupCallParticipantAdmin($0) }
dict[-1985949076] = { return Api.GroupCallParticipant.parse_groupCallParticipant($0) }
dict[1100680690] = { return Api.GroupCallParticipant.parse_groupCallParticipantLeft($0) }
dict[-1648085351] = { return Api.GroupCallParticipant.parse_groupCallParticipantKicked($0) }
dict[-874654354] = { return Api.GroupCallParticipant.parse_groupCallParticipantInvited($0) }
dict[1443858741] = { return Api.messages.SentEncryptedMessage.parse_sentEncryptedMessage($0) }
dict[-1802240206] = { return Api.messages.SentEncryptedMessage.parse_sentEncryptedFile($0) }
dict[1571494644] = { return Api.ExportedMessageLink.parse_exportedMessageLink($0) }
@ -258,6 +266,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-13975905] = { return Api.Update.parse_updateChannelUserTyping($0) }
dict[-309990731] = { return Api.Update.parse_updatePinnedMessages($0) }
dict[-2054649973] = { return Api.Update.parse_updatePinnedChannelMessages($0) }
dict[92188360] = { return Api.Update.parse_updateGroupCallParticipant($0) }
dict[-2046916883] = { return Api.Update.parse_updateGroupCall($0) }
dict[136574537] = { return Api.messages.VotesList.parse_votesList($0) }
dict[1558266229] = { return Api.PopularContact.parse_popularContact($0) }
dict[-373643672] = { return Api.FolderPeer.parse_folderPeer($0) }
@ -329,11 +339,11 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-392411726] = { return Api.WebPage.parse_webPage($0) }
dict[1930545681] = { return Api.WebPage.parse_webPageNotModified($0) }
dict[1036876423] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageText($0) }
dict[-190472735] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageMediaGeo($0) }
dict[1262639204] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageGame($0) }
dict[864077702] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageMediaAuto($0) }
dict[1098628881] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageMediaVenue($0) }
dict[-1494368259] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageMediaContact($0) }
dict[-1768777083] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageMediaGeo($0) }
dict[2002815875] = { return Api.KeyboardButtonRow.parse_keyboardButtonRow($0) }
dict[-290164953] = { return Api.StickerSet.parse_stickerSet($0) }
dict[354925740] = { return Api.SecureSecretSettings.parse_secureSecretSettings($0) }
@ -392,6 +402,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-666824391] = { return Api.payments.PaymentResult.parse_paymentVerificationNeeded($0) }
dict[1694474197] = { return Api.messages.Chats.parse_chats($0) }
dict[-1663561404] = { return Api.messages.Chats.parse_chatsSlice($0) }
dict[-659913713] = { return Api.InputGroupCall.parse_inputGroupCall($0) }
dict[482797855] = { return Api.InputSingleMedia.parse_inputSingleMedia($0) }
dict[1163625789] = { return Api.MessageViews.parse_messageViews($0) }
dict[218751099] = { return Api.InputPrivacyRule.parse_inputPrivacyValueAllowContacts($0) }
@ -496,6 +507,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1495959709] = { return Api.MessageReplyHeader.parse_messageReplyHeader($0) }
dict[411017418] = { return Api.SecureValue.parse_secureValue($0) }
dict[-316748368] = { return Api.SecureValueHash.parse_secureValueHash($0) }
dict[1731723191] = { return Api.phone.GroupCall.parse_groupCall($0) }
dict[-398136321] = { return Api.messages.SearchCounter.parse_searchCounter($0) }
dict[-2128698738] = { return Api.auth.CheckedPhone.parse_checkedPhone($0) }
dict[-1188055347] = { return Api.PageListItem.parse_pageListItemText($0) }
@ -777,6 +789,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-648257196] = { return Api.MessageAction.parse_messageActionSecureValuesSent($0) }
dict[-202219658] = { return Api.MessageAction.parse_messageActionContactSignUp($0) }
dict[-1730095465] = { return Api.MessageAction.parse_messageActionGeoProximityReached($0) }
dict[2047704898] = { return Api.MessageAction.parse_messageActionGroupCall($0) }
dict[1399245077] = { return Api.PhoneCall.parse_phoneCallEmpty($0) }
dict[462375633] = { return Api.PhoneCall.parse_phoneCallWaiting($0) }
dict[-2014659757] = { return Api.PhoneCall.parse_phoneCallRequested($0) }
@ -914,6 +927,8 @@ public struct Api {
switch object {
case let _1 as Api.messages.StickerSet:
_1.serialize(buffer, boxed)
case let _1 as Api.GroupCall:
_1.serialize(buffer, boxed)
case let _1 as Api.InputGeoPoint:
_1.serialize(buffer, boxed)
case let _1 as Api.payments.ValidatedRequestedInfo:
@ -994,6 +1009,8 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.messages.InactiveChats:
_1.serialize(buffer, boxed)
case let _1 as Api.GroupCallParticipant:
_1.serialize(buffer, boxed)
case let _1 as Api.messages.SentEncryptedMessage:
_1.serialize(buffer, boxed)
case let _1 as Api.ExportedMessageLink:
@ -1122,6 +1139,8 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.messages.Chats:
_1.serialize(buffer, boxed)
case let _1 as Api.InputGroupCall:
_1.serialize(buffer, boxed)
case let _1 as Api.InputSingleMedia:
_1.serialize(buffer, boxed)
case let _1 as Api.MessageViews:
@ -1206,6 +1225,8 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.SecureValueHash:
_1.serialize(buffer, boxed)
case let _1 as Api.phone.GroupCall:
_1.serialize(buffer, boxed)
case let _1 as Api.messages.SearchCounter:
_1.serialize(buffer, boxed)
case let _1 as Api.auth.CheckedPhone:

View File

@ -1909,6 +1909,134 @@ public struct messages {
}
}
public extension Api {
public enum GroupCall: TypeConstructorDescription {
case groupCallPrivate(flags: Int32, id: Int64, accessHash: Int64, channelId: Int32?, participantsCount: Int32, adminId: Int32)
case groupCall(flags: Int32, id: Int64, accessHash: Int64, channelId: Int32?, adminId: Int32, reflectorId: Int64, params: Api.DataJSON?)
case groupCallDiscarded(id: Int64, accessHash: Int64, duration: Int32)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .groupCallPrivate(let flags, let id, let accessHash, let channelId, let participantsCount, let adminId):
if boxed {
buffer.appendInt32(1829443076)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt64(id, buffer: buffer, boxed: false)
serializeInt64(accessHash, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(channelId!, buffer: buffer, boxed: false)}
serializeInt32(participantsCount, buffer: buffer, boxed: false)
serializeInt32(adminId, buffer: buffer, boxed: false)
break
case .groupCall(let flags, let id, let accessHash, let channelId, let adminId, let reflectorId, let params):
if boxed {
buffer.appendInt32(-857633264)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt64(id, buffer: buffer, boxed: false)
serializeInt64(accessHash, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(channelId!, buffer: buffer, boxed: false)}
serializeInt32(adminId, buffer: buffer, boxed: false)
serializeInt64(reflectorId, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 1) != 0 {params!.serialize(buffer, true)}
break
case .groupCallDiscarded(let id, let accessHash, let duration):
if boxed {
buffer.appendInt32(2004925620)
}
serializeInt64(id, buffer: buffer, boxed: false)
serializeInt64(accessHash, buffer: buffer, boxed: false)
serializeInt32(duration, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .groupCallPrivate(let flags, let id, let accessHash, let channelId, let participantsCount, let adminId):
return ("groupCallPrivate", [("flags", flags), ("id", id), ("accessHash", accessHash), ("channelId", channelId), ("participantsCount", participantsCount), ("adminId", adminId)])
case .groupCall(let flags, let id, let accessHash, let channelId, let adminId, let reflectorId, let params):
return ("groupCall", [("flags", flags), ("id", id), ("accessHash", accessHash), ("channelId", channelId), ("adminId", adminId), ("reflectorId", reflectorId), ("params", params)])
case .groupCallDiscarded(let id, let accessHash, let duration):
return ("groupCallDiscarded", [("id", id), ("accessHash", accessHash), ("duration", duration)])
}
}
public static func parse_groupCallPrivate(_ reader: BufferReader) -> GroupCall? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Int64?
_2 = reader.readInt64()
var _3: Int64?
_3 = reader.readInt64()
var _4: Int32?
if Int(_1!) & Int(1 << 0) != 0 {_4 = reader.readInt32() }
var _5: Int32?
_5 = reader.readInt32()
var _6: Int32?
_6 = reader.readInt32()
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil
let _c5 = _5 != nil
let _c6 = _6 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
return Api.GroupCall.groupCallPrivate(flags: _1!, id: _2!, accessHash: _3!, channelId: _4, participantsCount: _5!, adminId: _6!)
}
else {
return nil
}
}
public static func parse_groupCall(_ reader: BufferReader) -> GroupCall? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Int64?
_2 = reader.readInt64()
var _3: Int64?
_3 = reader.readInt64()
var _4: Int32?
if Int(_1!) & Int(1 << 0) != 0 {_4 = reader.readInt32() }
var _5: Int32?
_5 = reader.readInt32()
var _6: Int64?
_6 = reader.readInt64()
var _7: Api.DataJSON?
if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() {
_7 = Api.parse(reader, signature: signature) as? Api.DataJSON
} }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil
let _c5 = _5 != nil
let _c6 = _6 != nil
let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
return Api.GroupCall.groupCall(flags: _1!, id: _2!, accessHash: _3!, channelId: _4, adminId: _5!, reflectorId: _6!, params: _7)
}
else {
return nil
}
}
public static func parse_groupCallDiscarded(_ reader: BufferReader) -> GroupCall? {
var _1: Int64?
_1 = reader.readInt64()
var _2: Int64?
_2 = reader.readInt64()
var _3: Int32?
_3 = reader.readInt32()
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
if _c1 && _c2 && _c3 {
return Api.GroupCall.groupCallDiscarded(id: _1!, accessHash: _2!, duration: _3!)
}
else {
return nil
}
}
}
public enum InputGeoPoint: TypeConstructorDescription {
case inputGeoPointEmpty
case inputGeoPoint(flags: Int32, lat: Double, long: Double, accuracyRadius: Int32?)
@ -1969,7 +2097,7 @@ public extension Api {
}
public enum ChatFull: TypeConstructorDescription {
case chatFull(flags: Int32, id: Int32, about: String, participants: Api.ChatParticipants, chatPhoto: Api.Photo?, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite, botInfo: [Api.BotInfo]?, pinnedMsgId: Int32?, folderId: Int32?)
case channelFull(flags: Int32, id: Int32, about: String, participantsCount: Int32?, adminsCount: Int32?, kickedCount: Int32?, bannedCount: Int32?, onlineCount: Int32?, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, chatPhoto: Api.Photo, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite, botInfo: [Api.BotInfo], migratedFromChatId: Int32?, migratedFromMaxId: Int32?, pinnedMsgId: Int32?, stickerset: Api.StickerSet?, availableMinId: Int32?, folderId: Int32?, linkedChatId: Int32?, location: Api.ChannelLocation?, slowmodeSeconds: Int32?, slowmodeNextSendDate: Int32?, statsDc: Int32?, pts: Int32)
case channelFull(flags: Int32, id: Int32, about: String, participantsCount: Int32?, adminsCount: Int32?, kickedCount: Int32?, bannedCount: Int32?, onlineCount: Int32?, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, chatPhoto: Api.Photo, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite, botInfo: [Api.BotInfo], migratedFromChatId: Int32?, migratedFromMaxId: Int32?, pinnedMsgId: Int32?, stickerset: Api.StickerSet?, availableMinId: Int32?, folderId: Int32?, linkedChatId: Int32?, location: Api.ChannelLocation?, slowmodeSeconds: Int32?, slowmodeNextSendDate: Int32?, statsDc: Int32?, pts: Int32, callMsgId: Int32?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
@ -1992,9 +2120,9 @@ public extension Api {
if Int(flags) & Int(1 << 6) != 0 {serializeInt32(pinnedMsgId!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 11) != 0 {serializeInt32(folderId!, buffer: buffer, boxed: false)}
break
case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let statsDc, let pts):
case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let statsDc, let pts, let callMsgId):
if boxed {
buffer.appendInt32(-253335766)
buffer.appendInt32(-428758403)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt32(id, buffer: buffer, boxed: false)
@ -2027,6 +2155,7 @@ public extension Api {
if Int(flags) & Int(1 << 18) != 0 {serializeInt32(slowmodeNextSendDate!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 12) != 0 {serializeInt32(statsDc!, buffer: buffer, boxed: false)}
serializeInt32(pts, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 21) != 0 {serializeInt32(callMsgId!, buffer: buffer, boxed: false)}
break
}
}
@ -2035,8 +2164,8 @@ public extension Api {
switch self {
case .chatFull(let flags, let id, let about, let participants, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let pinnedMsgId, let folderId):
return ("chatFull", [("flags", flags), ("id", id), ("about", about), ("participants", participants), ("chatPhoto", chatPhoto), ("notifySettings", notifySettings), ("exportedInvite", exportedInvite), ("botInfo", botInfo), ("pinnedMsgId", pinnedMsgId), ("folderId", folderId)])
case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let statsDc, let pts):
return ("channelFull", [("flags", flags), ("id", id), ("about", about), ("participantsCount", participantsCount), ("adminsCount", adminsCount), ("kickedCount", kickedCount), ("bannedCount", bannedCount), ("onlineCount", onlineCount), ("readInboxMaxId", readInboxMaxId), ("readOutboxMaxId", readOutboxMaxId), ("unreadCount", unreadCount), ("chatPhoto", chatPhoto), ("notifySettings", notifySettings), ("exportedInvite", exportedInvite), ("botInfo", botInfo), ("migratedFromChatId", migratedFromChatId), ("migratedFromMaxId", migratedFromMaxId), ("pinnedMsgId", pinnedMsgId), ("stickerset", stickerset), ("availableMinId", availableMinId), ("folderId", folderId), ("linkedChatId", linkedChatId), ("location", location), ("slowmodeSeconds", slowmodeSeconds), ("slowmodeNextSendDate", slowmodeNextSendDate), ("statsDc", statsDc), ("pts", pts)])
case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let statsDc, let pts, let callMsgId):
return ("channelFull", [("flags", flags), ("id", id), ("about", about), ("participantsCount", participantsCount), ("adminsCount", adminsCount), ("kickedCount", kickedCount), ("bannedCount", bannedCount), ("onlineCount", onlineCount), ("readInboxMaxId", readInboxMaxId), ("readOutboxMaxId", readOutboxMaxId), ("unreadCount", unreadCount), ("chatPhoto", chatPhoto), ("notifySettings", notifySettings), ("exportedInvite", exportedInvite), ("botInfo", botInfo), ("migratedFromChatId", migratedFromChatId), ("migratedFromMaxId", migratedFromMaxId), ("pinnedMsgId", pinnedMsgId), ("stickerset", stickerset), ("availableMinId", availableMinId), ("folderId", folderId), ("linkedChatId", linkedChatId), ("location", location), ("slowmodeSeconds", slowmodeSeconds), ("slowmodeNextSendDate", slowmodeNextSendDate), ("statsDc", statsDc), ("pts", pts), ("callMsgId", callMsgId)])
}
}
@ -2155,6 +2284,8 @@ public extension Api {
if Int(_1!) & Int(1 << 12) != 0 {_26 = reader.readInt32() }
var _27: Int32?
_27 = reader.readInt32()
var _28: Int32?
if Int(_1!) & Int(1 << 21) != 0 {_28 = reader.readInt32() }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
@ -2182,8 +2313,9 @@ public extension Api {
let _c25 = (Int(_1!) & Int(1 << 18) == 0) || _25 != nil
let _c26 = (Int(_1!) & Int(1 << 12) == 0) || _26 != nil
let _c27 = _27 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 && _c23 && _c24 && _c25 && _c26 && _c27 {
return Api.ChatFull.channelFull(flags: _1!, id: _2!, about: _3!, participantsCount: _4, adminsCount: _5, kickedCount: _6, bannedCount: _7, onlineCount: _8, readInboxMaxId: _9!, readOutboxMaxId: _10!, unreadCount: _11!, chatPhoto: _12!, notifySettings: _13!, exportedInvite: _14!, botInfo: _15!, migratedFromChatId: _16, migratedFromMaxId: _17, pinnedMsgId: _18, stickerset: _19, availableMinId: _20, folderId: _21, linkedChatId: _22, location: _23, slowmodeSeconds: _24, slowmodeNextSendDate: _25, statsDc: _26, pts: _27!)
let _c28 = (Int(_1!) & Int(1 << 21) == 0) || _28 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 && _c23 && _c24 && _c25 && _c26 && _c27 && _c28 {
return Api.ChatFull.channelFull(flags: _1!, id: _2!, about: _3!, participantsCount: _4, adminsCount: _5, kickedCount: _6, bannedCount: _7, onlineCount: _8, readInboxMaxId: _9!, readOutboxMaxId: _10!, unreadCount: _11!, chatPhoto: _12!, notifySettings: _13!, exportedInvite: _14!, botInfo: _15!, migratedFromChatId: _16, migratedFromMaxId: _17, pinnedMsgId: _18, stickerset: _19, availableMinId: _20, folderId: _21, linkedChatId: _22, location: _23, slowmodeSeconds: _24, slowmodeNextSendDate: _25, statsDc: _26, pts: _27!, callMsgId: _28)
}
else {
return nil
@ -5266,6 +5398,148 @@ public extension Api {
}
}
}
public enum GroupCallParticipant: TypeConstructorDescription {
case groupCallParticipantAdmin(userId: Int32, source: Int32)
case groupCallParticipant(flags: Int32, userId: Int32, date: Int32, source: Int32)
case groupCallParticipantLeft(userId: Int32)
case groupCallParticipantKicked(userId: Int32)
case groupCallParticipantInvited(flags: Int32, userId: Int32, inviterId: Int32, date: Int32)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .groupCallParticipantAdmin(let userId, let source):
if boxed {
buffer.appendInt32(-1513019911)
}
serializeInt32(userId, buffer: buffer, boxed: false)
serializeInt32(source, buffer: buffer, boxed: false)
break
case .groupCallParticipant(let flags, let userId, let date, let source):
if boxed {
buffer.appendInt32(-1985949076)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt32(userId, buffer: buffer, boxed: false)
serializeInt32(date, buffer: buffer, boxed: false)
serializeInt32(source, buffer: buffer, boxed: false)
break
case .groupCallParticipantLeft(let userId):
if boxed {
buffer.appendInt32(1100680690)
}
serializeInt32(userId, buffer: buffer, boxed: false)
break
case .groupCallParticipantKicked(let userId):
if boxed {
buffer.appendInt32(-1648085351)
}
serializeInt32(userId, buffer: buffer, boxed: false)
break
case .groupCallParticipantInvited(let flags, let userId, let inviterId, let date):
if boxed {
buffer.appendInt32(-874654354)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt32(userId, buffer: buffer, boxed: false)
serializeInt32(inviterId, buffer: buffer, boxed: false)
serializeInt32(date, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .groupCallParticipantAdmin(let userId, let source):
return ("groupCallParticipantAdmin", [("userId", userId), ("source", source)])
case .groupCallParticipant(let flags, let userId, let date, let source):
return ("groupCallParticipant", [("flags", flags), ("userId", userId), ("date", date), ("source", source)])
case .groupCallParticipantLeft(let userId):
return ("groupCallParticipantLeft", [("userId", userId)])
case .groupCallParticipantKicked(let userId):
return ("groupCallParticipantKicked", [("userId", userId)])
case .groupCallParticipantInvited(let flags, let userId, let inviterId, let date):
return ("groupCallParticipantInvited", [("flags", flags), ("userId", userId), ("inviterId", inviterId), ("date", date)])
}
}
public static func parse_groupCallParticipantAdmin(_ reader: BufferReader) -> GroupCallParticipant? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Int32?
_2 = reader.readInt32()
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.GroupCallParticipant.groupCallParticipantAdmin(userId: _1!, source: _2!)
}
else {
return nil
}
}
public static func parse_groupCallParticipant(_ reader: BufferReader) -> GroupCallParticipant? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Int32?
_2 = reader.readInt32()
var _3: Int32?
_3 = reader.readInt32()
var _4: Int32?
_4 = reader.readInt32()
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = _4 != nil
if _c1 && _c2 && _c3 && _c4 {
return Api.GroupCallParticipant.groupCallParticipant(flags: _1!, userId: _2!, date: _3!, source: _4!)
}
else {
return nil
}
}
public static func parse_groupCallParticipantLeft(_ reader: BufferReader) -> GroupCallParticipant? {
var _1: Int32?
_1 = reader.readInt32()
let _c1 = _1 != nil
if _c1 {
return Api.GroupCallParticipant.groupCallParticipantLeft(userId: _1!)
}
else {
return nil
}
}
public static func parse_groupCallParticipantKicked(_ reader: BufferReader) -> GroupCallParticipant? {
var _1: Int32?
_1 = reader.readInt32()
let _c1 = _1 != nil
if _c1 {
return Api.GroupCallParticipant.groupCallParticipantKicked(userId: _1!)
}
else {
return nil
}
}
public static func parse_groupCallParticipantInvited(_ reader: BufferReader) -> GroupCallParticipant? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Int32?
_2 = reader.readInt32()
var _3: Int32?
_3 = reader.readInt32()
var _4: Int32?
_4 = reader.readInt32()
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = _4 != nil
if _c1 && _c2 && _c3 && _c4 {
return Api.GroupCallParticipant.groupCallParticipantInvited(flags: _1!, userId: _2!, inviterId: _3!, date: _4!)
}
else {
return nil
}
}
}
public enum ExportedMessageLink: TypeConstructorDescription {
case exportedMessageLink(link: String, html: String)
@ -6193,6 +6467,8 @@ public extension Api {
case updateChannelUserTyping(flags: Int32, channelId: Int32, topMsgId: Int32?, userId: Int32, action: Api.SendMessageAction)
case updatePinnedMessages(flags: Int32, peer: Api.Peer, messages: [Int32], pts: Int32, ptsCount: Int32)
case updatePinnedChannelMessages(flags: Int32, channelId: Int32, messages: [Int32], pts: Int32, ptsCount: Int32)
case updateGroupCallParticipant(call: Api.InputGroupCall, participant: Api.GroupCallParticipant)
case updateGroupCall(call: Api.GroupCall)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
@ -6928,6 +7204,19 @@ public extension Api {
serializeInt32(pts, buffer: buffer, boxed: false)
serializeInt32(ptsCount, buffer: buffer, boxed: false)
break
case .updateGroupCallParticipant(let call, let participant):
if boxed {
buffer.appendInt32(92188360)
}
call.serialize(buffer, true)
participant.serialize(buffer, true)
break
case .updateGroupCall(let call):
if boxed {
buffer.appendInt32(-2046916883)
}
call.serialize(buffer, true)
break
}
}
@ -7103,6 +7392,10 @@ public extension Api {
return ("updatePinnedMessages", [("flags", flags), ("peer", peer), ("messages", messages), ("pts", pts), ("ptsCount", ptsCount)])
case .updatePinnedChannelMessages(let flags, let channelId, let messages, let pts, let ptsCount):
return ("updatePinnedChannelMessages", [("flags", flags), ("channelId", channelId), ("messages", messages), ("pts", pts), ("ptsCount", ptsCount)])
case .updateGroupCallParticipant(let call, let participant):
return ("updateGroupCallParticipant", [("call", call), ("participant", participant)])
case .updateGroupCall(let call):
return ("updateGroupCall", [("call", call)])
}
}
@ -8575,6 +8868,37 @@ public extension Api {
return nil
}
}
public static func parse_updateGroupCallParticipant(_ reader: BufferReader) -> Update? {
var _1: Api.InputGroupCall?
if let signature = reader.readInt32() {
_1 = Api.parse(reader, signature: signature) as? Api.InputGroupCall
}
var _2: Api.GroupCallParticipant?
if let signature = reader.readInt32() {
_2 = Api.parse(reader, signature: signature) as? Api.GroupCallParticipant
}
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.Update.updateGroupCallParticipant(call: _1!, participant: _2!)
}
else {
return nil
}
}
public static func parse_updateGroupCall(_ reader: BufferReader) -> Update? {
var _1: Api.GroupCall?
if let signature = reader.readInt32() {
_1 = Api.parse(reader, signature: signature) as? Api.GroupCall
}
let _c1 = _1 != nil
if _c1 {
return Api.Update.updateGroupCall(call: _1!)
}
else {
return nil
}
}
}
public enum PopularContact: TypeConstructorDescription {
@ -10399,11 +10723,11 @@ public extension Api {
}
public enum InputBotInlineMessage: TypeConstructorDescription {
case inputBotInlineMessageText(flags: Int32, message: String, entities: [Api.MessageEntity]?, replyMarkup: Api.ReplyMarkup?)
case inputBotInlineMessageMediaGeo(flags: Int32, geoPoint: Api.InputGeoPoint, replyMarkup: Api.ReplyMarkup?)
case inputBotInlineMessageGame(flags: Int32, replyMarkup: Api.ReplyMarkup?)
case inputBotInlineMessageMediaAuto(flags: Int32, message: String, entities: [Api.MessageEntity]?, replyMarkup: Api.ReplyMarkup?)
case inputBotInlineMessageMediaVenue(flags: Int32, geoPoint: Api.InputGeoPoint, title: String, address: String, provider: String, venueId: String, venueType: String, replyMarkup: Api.ReplyMarkup?)
case inputBotInlineMessageMediaContact(flags: Int32, phoneNumber: String, firstName: String, lastName: String, vcard: String, replyMarkup: Api.ReplyMarkup?)
case inputBotInlineMessageMediaGeo(flags: Int32, geoPoint: Api.InputGeoPoint, heading: Int32?, period: Int32?, proximityNotificationRadius: Int32?, replyMarkup: Api.ReplyMarkup?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
@ -10420,14 +10744,6 @@ public extension Api {
}}
if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)}
break
case .inputBotInlineMessageMediaGeo(let flags, let geoPoint, let replyMarkup):
if boxed {
buffer.appendInt32(-190472735)
}
serializeInt32(flags, buffer: buffer, boxed: false)
geoPoint.serialize(buffer, true)
if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)}
break
case .inputBotInlineMessageGame(let flags, let replyMarkup):
if boxed {
buffer.appendInt32(1262639204)
@ -10472,6 +10788,17 @@ public extension Api {
serializeString(vcard, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)}
break
case .inputBotInlineMessageMediaGeo(let flags, let geoPoint, let heading, let period, let proximityNotificationRadius, let replyMarkup):
if boxed {
buffer.appendInt32(-1768777083)
}
serializeInt32(flags, buffer: buffer, boxed: false)
geoPoint.serialize(buffer, true)
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(heading!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(period!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 3) != 0 {serializeInt32(proximityNotificationRadius!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)}
break
}
}
@ -10479,8 +10806,6 @@ public extension Api {
switch self {
case .inputBotInlineMessageText(let flags, let message, let entities, let replyMarkup):
return ("inputBotInlineMessageText", [("flags", flags), ("message", message), ("entities", entities), ("replyMarkup", replyMarkup)])
case .inputBotInlineMessageMediaGeo(let flags, let geoPoint, let replyMarkup):
return ("inputBotInlineMessageMediaGeo", [("flags", flags), ("geoPoint", geoPoint), ("replyMarkup", replyMarkup)])
case .inputBotInlineMessageGame(let flags, let replyMarkup):
return ("inputBotInlineMessageGame", [("flags", flags), ("replyMarkup", replyMarkup)])
case .inputBotInlineMessageMediaAuto(let flags, let message, let entities, let replyMarkup):
@ -10489,6 +10814,8 @@ public extension Api {
return ("inputBotInlineMessageMediaVenue", [("flags", flags), ("geoPoint", geoPoint), ("title", title), ("address", address), ("provider", provider), ("venueId", venueId), ("venueType", venueType), ("replyMarkup", replyMarkup)])
case .inputBotInlineMessageMediaContact(let flags, let phoneNumber, let firstName, let lastName, let vcard, let replyMarkup):
return ("inputBotInlineMessageMediaContact", [("flags", flags), ("phoneNumber", phoneNumber), ("firstName", firstName), ("lastName", lastName), ("vcard", vcard), ("replyMarkup", replyMarkup)])
case .inputBotInlineMessageMediaGeo(let flags, let geoPoint, let heading, let period, let proximityNotificationRadius, let replyMarkup):
return ("inputBotInlineMessageMediaGeo", [("flags", flags), ("geoPoint", geoPoint), ("heading", heading), ("period", period), ("proximityNotificationRadius", proximityNotificationRadius), ("replyMarkup", replyMarkup)])
}
}
@ -10516,27 +10843,6 @@ public extension Api {
return nil
}
}
public static func parse_inputBotInlineMessageMediaGeo(_ reader: BufferReader) -> InputBotInlineMessage? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Api.InputGeoPoint?
if let signature = reader.readInt32() {
_2 = Api.parse(reader, signature: signature) as? Api.InputGeoPoint
}
var _3: Api.ReplyMarkup?
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
_3 = Api.parse(reader, signature: signature) as? Api.ReplyMarkup
} }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = (Int(_1!) & Int(1 << 2) == 0) || _3 != nil
if _c1 && _c2 && _c3 {
return Api.InputBotInlineMessage.inputBotInlineMessageMediaGeo(flags: _1!, geoPoint: _2!, replyMarkup: _3)
}
else {
return nil
}
}
public static func parse_inputBotInlineMessageGame(_ reader: BufferReader) -> InputBotInlineMessage? {
var _1: Int32?
_1 = reader.readInt32()
@ -10641,6 +10947,36 @@ public extension Api {
return nil
}
}
public static func parse_inputBotInlineMessageMediaGeo(_ reader: BufferReader) -> InputBotInlineMessage? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Api.InputGeoPoint?
if let signature = reader.readInt32() {
_2 = Api.parse(reader, signature: signature) as? Api.InputGeoPoint
}
var _3: Int32?
if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() }
var _4: Int32?
if Int(_1!) & Int(1 << 1) != 0 {_4 = reader.readInt32() }
var _5: Int32?
if Int(_1!) & Int(1 << 3) != 0 {_5 = reader.readInt32() }
var _6: Api.ReplyMarkup?
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
_6 = Api.parse(reader, signature: signature) as? Api.ReplyMarkup
} }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil
let _c5 = (Int(_1!) & Int(1 << 3) == 0) || _5 != nil
let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
return Api.InputBotInlineMessage.inputBotInlineMessageMediaGeo(flags: _1!, geoPoint: _2!, heading: _3, period: _4, proximityNotificationRadius: _5, replyMarkup: _6)
}
else {
return nil
}
}
}
public enum KeyboardButtonRow: TypeConstructorDescription {
@ -12048,6 +12384,44 @@ public extension Api {
}
}
}
public enum InputGroupCall: TypeConstructorDescription {
case inputGroupCall(id: Int64, accessHash: Int64)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .inputGroupCall(let id, let accessHash):
if boxed {
buffer.appendInt32(-659913713)
}
serializeInt64(id, buffer: buffer, boxed: false)
serializeInt64(accessHash, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .inputGroupCall(let id, let accessHash):
return ("inputGroupCall", [("id", id), ("accessHash", accessHash)])
}
}
public static func parse_inputGroupCall(_ reader: BufferReader) -> InputGroupCall? {
var _1: Int64?
_1 = reader.readInt64()
var _2: Int64?
_2 = reader.readInt64()
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.InputGroupCall.inputGroupCall(id: _1!, accessHash: _2!)
}
else {
return nil
}
}
}
public enum InputSingleMedia: TypeConstructorDescription {
case inputSingleMedia(flags: Int32, media: Api.InputMedia, randomId: Int64, message: String, entities: [Api.MessageEntity]?)
@ -21186,6 +21560,7 @@ public extension Api {
case messageActionSecureValuesSent(types: [Api.SecureValueType])
case messageActionContactSignUp
case messageActionGeoProximityReached(fromId: Api.Peer, toId: Api.Peer, distance: Int32)
case messageActionGroupCall(flags: Int32, call: Api.InputGroupCall, duration: Int32?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
@ -21365,6 +21740,14 @@ public extension Api {
toId.serialize(buffer, true)
serializeInt32(distance, buffer: buffer, boxed: false)
break
case .messageActionGroupCall(let flags, let call, let duration):
if boxed {
buffer.appendInt32(2047704898)
}
serializeInt32(flags, buffer: buffer, boxed: false)
call.serialize(buffer, true)
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(duration!, buffer: buffer, boxed: false)}
break
}
}
@ -21418,6 +21801,8 @@ public extension Api {
return ("messageActionContactSignUp", [])
case .messageActionGeoProximityReached(let fromId, let toId, let distance):
return ("messageActionGeoProximityReached", [("fromId", fromId), ("toId", toId), ("distance", distance)])
case .messageActionGroupCall(let flags, let call, let duration):
return ("messageActionGroupCall", [("flags", flags), ("call", call), ("duration", duration)])
}
}
@ -21707,6 +22092,25 @@ public extension Api {
return nil
}
}
public static func parse_messageActionGroupCall(_ reader: BufferReader) -> MessageAction? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Api.InputGroupCall?
if let signature = reader.readInt32() {
_2 = Api.parse(reader, signature: signature) as? Api.InputGroupCall
}
var _3: Int32?
if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
if _c1 && _c2 && _c3 {
return Api.MessageAction.messageActionGroupCall(flags: _1!, call: _2!, duration: _3)
}
else {
return nil
}
}
}
public enum PhoneCall: TypeConstructorDescription {

View File

@ -1648,6 +1648,72 @@ public struct photos {
}
public extension Api {
public struct phone {
public enum GroupCall: TypeConstructorDescription {
case groupCall(call: Api.GroupCall, participants: [Api.GroupCallParticipant], chats: [Api.Chat], users: [Api.User])
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .groupCall(let call, let participants, let chats, let users):
if boxed {
buffer.appendInt32(1731723191)
}
call.serialize(buffer, true)
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(participants.count))
for item in participants {
item.serialize(buffer, true)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(chats.count))
for item in chats {
item.serialize(buffer, true)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(users.count))
for item in users {
item.serialize(buffer, true)
}
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .groupCall(let call, let participants, let chats, let users):
return ("groupCall", [("call", call), ("participants", participants), ("chats", chats), ("users", users)])
}
}
public static func parse_groupCall(_ reader: BufferReader) -> GroupCall? {
var _1: Api.GroupCall?
if let signature = reader.readInt32() {
_1 = Api.parse(reader, signature: signature) as? Api.GroupCall
}
var _2: [Api.GroupCallParticipant]?
if let _ = reader.readInt32() {
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.GroupCallParticipant.self)
}
var _3: [Api.Chat]?
if let _ = reader.readInt32() {
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
}
var _4: [Api.User]?
if let _ = reader.readInt32() {
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
}
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = _4 != nil
if _c1 && _c2 && _c3 && _c4 {
return Api.phone.GroupCall.groupCall(call: _1!, participants: _2!, chats: _3!, users: _4!)
}
else {
return nil
}
}
}
public enum PhoneCall: TypeConstructorDescription {
case phoneCall(phoneCall: Api.PhoneCall, users: [Api.User])
@ -7118,6 +7184,111 @@ public extension Api {
return result
})
}
public static func createGroupCall(flags: Int32, channel: Api.InputChannel, randomId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
let buffer = Buffer()
buffer.appendInt32(-1542553507)
serializeInt32(flags, buffer: buffer, boxed: false)
channel.serialize(buffer, true)
serializeInt32(randomId, buffer: buffer, boxed: false)
return (FunctionDescription(name: "phone.createGroupCall", parameters: [("flags", flags), ("channel", channel), ("randomId", randomId)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
let reader = BufferReader(buffer)
var result: Api.Updates?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.Updates
}
return result
})
}
public static func joinGroupCall(call: Api.InputGroupCall, params: Api.DataJSON) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
let buffer = Buffer()
buffer.appendInt32(893342305)
call.serialize(buffer, true)
params.serialize(buffer, true)
return (FunctionDescription(name: "phone.joinGroupCall", parameters: [("call", call), ("params", params)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
let reader = BufferReader(buffer)
var result: Api.Updates?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.Updates
}
return result
})
}
public static func leaveGroupCall(call: Api.InputGroupCall) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
let buffer = Buffer()
buffer.appendInt32(1625919071)
call.serialize(buffer, true)
return (FunctionDescription(name: "phone.leaveGroupCall", parameters: [("call", call)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
let reader = BufferReader(buffer)
var result: Api.Updates?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.Updates
}
return result
})
}
public static func editGroupCallMember(flags: Int32, call: Api.InputGroupCall, userId: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
let buffer = Buffer()
buffer.appendInt32(1662282468)
serializeInt32(flags, buffer: buffer, boxed: false)
call.serialize(buffer, true)
userId.serialize(buffer, true)
return (FunctionDescription(name: "phone.editGroupCallMember", parameters: [("flags", flags), ("call", call), ("userId", userId)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
let reader = BufferReader(buffer)
var result: Api.Updates?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.Updates
}
return result
})
}
public static func kickGroupCallMember(flags: Int32, call: Api.InputGroupCall, userId: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
let buffer = Buffer()
buffer.appendInt32(-1731080446)
serializeInt32(flags, buffer: buffer, boxed: false)
call.serialize(buffer, true)
userId.serialize(buffer, true)
return (FunctionDescription(name: "phone.kickGroupCallMember", parameters: [("flags", flags), ("call", call), ("userId", userId)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
let reader = BufferReader(buffer)
var result: Api.Updates?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.Updates
}
return result
})
}
public static func discardGroupCall(call: Api.InputGroupCall) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
let buffer = Buffer()
buffer.appendInt32(2054648117)
call.serialize(buffer, true)
return (FunctionDescription(name: "phone.discardGroupCall", parameters: [("call", call)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
let reader = BufferReader(buffer)
var result: Api.Updates?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.Updates
}
return result
})
}
public static func getGroupCall(call: Api.InputGroupCall) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.phone.GroupCall>) {
let buffer = Buffer()
buffer.appendInt32(209498135)
call.serialize(buffer, true)
return (FunctionDescription(name: "phone.getGroupCall", parameters: [("call", call)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.phone.GroupCall? in
let reader = BufferReader(buffer)
var result: Api.phone.GroupCall?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.phone.GroupCall
}
return result
})
}
}
}
}

View File

@ -60,6 +60,11 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
private var currentCallDisposable = MetaDisposable()
private let removeCurrentCallDisposable = MetaDisposable()
private var currentGroupCallValue: PresentationGroupCallImpl?
private var currentGroupCall: PresentationGroupCallImpl? {
return self.currentGroupCallValue
}
private var ringingStatesDisposable: Disposable?
private let hasActiveCallsPromise = ValuePromise<Bool>(false, ignoreRepeated: true)
@ -72,6 +77,11 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
return self.currentCallPromise.get()
}
private let currentGroupCallPromise = Promise<PresentationGroupCall?>(nil)
public var currentGroupCallSignal: Signal<PresentationGroupCall?, NoError> {
return self.currentGroupCallPromise.get()
}
private let startCallDisposable = MetaDisposable()
private var proxyServer: ProxyServerSettings?
@ -566,4 +576,100 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
self.resumeMediaPlayback()
}
}
private func updateCurrentGroupCall(_ value: PresentationGroupCallImpl?) {
let wasEmpty = self.currentGroupCallValue == nil
let isEmpty = value == nil
if wasEmpty && !isEmpty {
self.resumeMedia = self.isMediaPlaying()
}
self.currentGroupCallValue = value
if !wasEmpty && isEmpty && self.resumeMedia {
self.resumeMedia = false
self.resumeMediaPlayback()
}
}
public func requestOrJoinGroupCall(context: AccountContext, peerId: PeerId) {
let begin: () -> Void = { [weak self] in
guard let strongSelf = self else {
return
}
let _ = strongSelf.startGroupCall(account: context.account, peerId: peerId).start()
}
begin()
}
private func startGroupCall(
account: Account,
peerId: PeerId,
internalId: CallSessionInternalId = CallSessionInternalId()
) -> Signal<Bool, NoError> {
let (presentationData, present, openSettings) = self.getDeviceAccessData()
let isVideo = false
let accessEnabledSignal: Signal<Bool, NoError> = Signal { subscriber in
DeviceAccess.authorizeAccess(to: .microphone(.voiceCall), presentationData: presentationData, present: { c, a in
present(c, a)
}, openSettings: {
openSettings()
}, { value in
if isVideo && value {
DeviceAccess.authorizeAccess(to: .camera(.videoCall), presentationData: presentationData, present: { c, a in
present(c, a)
}, openSettings: {
openSettings()
}, { value in
subscriber.putNext(value)
subscriber.putCompletion()
})
} else {
subscriber.putNext(value)
subscriber.putCompletion()
}
})
return EmptyDisposable
}
|> runOn(Queue.mainQueue())
return accessEnabledSignal
|> deliverOnMainQueue
|> mapToSignal { [weak self] accessEnabled -> Signal<Bool, NoError> in
guard let strongSelf = self else {
return .single(false)
}
if !accessEnabled {
return .single(false)
}
let call = PresentationGroupCallImpl(
account: account,
audioSession: strongSelf.audioSession,
callKitIntegration: nil,
getDeviceAccessData: strongSelf.getDeviceAccessData,
internalId: internalId,
peerId: peerId,
peer: nil
)
strongSelf.updateCurrentGroupCall(call)
strongSelf.currentGroupCallPromise.set(.single(call))
strongSelf.hasActiveCallsPromise.set(true)
strongSelf.removeCurrentCallDisposable.set((call.canBeRemoved.get()
|> deliverOnMainQueue).start(next: { [weak call] value in
if value, let strongSelf = self, let call = call {
if strongSelf.currentGroupCall === call {
strongSelf.updateCurrentGroupCall(nil)
strongSelf.currentGroupCallPromise.set(.single(nil))
strongSelf.hasActiveCallsPromise.set(false)
}
}
}))
return .single(true)
}
}
}

View File

@ -0,0 +1,346 @@
import Foundation
import UIKit
import Postbox
import TelegramCore
import SyncCore
import SwiftSignalKit
import Display
import AVFoundation
import TelegramVoip
import TelegramAudio
import TelegramUIPreferences
import TelegramPresentationData
import DeviceAccess
import UniversalMediaPlayer
import AccountContext
public final class PresentationGroupCallImpl: PresentationGroupCall {
private enum InternalState {
case requesting
case active(GroupCallInfo)
case estabilished(GroupCallInfo, String, [Int32])
var callInfo: GroupCallInfo? {
switch self {
case .requesting:
return nil
case let .active(info):
return info
case let .estabilished(info, _, _):
return info
}
}
}
public let account: Account
private let audioSession: ManagedAudioSession
private let callKitIntegration: CallKitIntegration?
public var isIntegratedWithCallKit: Bool {
return self.callKitIntegration != nil
}
private let getDeviceAccessData: () -> (presentationData: PresentationData, present: (ViewController, Any?) -> Void, openSettings: () -> Void)
public let internalId: CallSessionInternalId
public let peerId: PeerId
public let peer: Peer?
private var internalState: InternalState = .requesting
private var callContext: OngoingGroupCallContext?
private var sessionStateDisposable: Disposable?
private let isMutedPromise = ValuePromise<Bool>(false)
private var isMutedValue = false
public var isMuted: Signal<Bool, NoError> {
return self.isMutedPromise.get()
}
private let audioOutputStatePromise = Promise<([AudioSessionOutput], AudioSessionOutput?)>(([], nil))
private var audioOutputStateValue: ([AudioSessionOutput], AudioSessionOutput?) = ([], nil)
private var currentAudioOutputValue: AudioSessionOutput = .builtin
public var audioOutputState: Signal<([AudioSessionOutput], AudioSessionOutput?), NoError> {
return self.audioOutputStatePromise.get()
}
private var audioSessionControl: ManagedAudioSessionControl?
private var audioSessionDisposable: Disposable?
private let audioSessionShouldBeActive = ValuePromise<Bool>(false, ignoreRepeated: true)
private var audioSessionShouldBeActiveDisposable: Disposable?
private let audioSessionActive = Promise<Bool>(false)
private var audioSessionActiveDisposable: Disposable?
private var isAudioSessionActive = false
let canBeRemoved = Promise<Bool>(false)
private let requestDisposable = MetaDisposable()
private var groupCallParticipantUpdatesDisposable: Disposable?
init(
account: Account,
audioSession: ManagedAudioSession,
callKitIntegration: CallKitIntegration?,
getDeviceAccessData: @escaping () -> (presentationData: PresentationData, present: (ViewController, Any?) -> Void, openSettings: () -> Void),
internalId: CallSessionInternalId,
peerId: PeerId,
peer: Peer?
) {
self.account = account
self.audioSession = audioSession
self.callKitIntegration = callKitIntegration
self.getDeviceAccessData = getDeviceAccessData
self.internalId = internalId
self.peerId = peerId
self.peer = peer
var didReceiveAudioOutputs = false
self.audioSessionDisposable = audioSession.push(audioSessionType: .voiceCall, manualActivate: { [weak self] control in
Queue.mainQueue().async {
if let strongSelf = self {
strongSelf.updateSessionState(internalState: strongSelf.internalState, audioSessionControl: control)
}
}
}, deactivate: { [weak self] in
return Signal { subscriber in
Queue.mainQueue().async {
if let strongSelf = self {
strongSelf.updateIsAudioSessionActive(false)
strongSelf.updateSessionState(internalState: strongSelf.internalState, audioSessionControl: nil)
}
subscriber.putCompletion()
}
return EmptyDisposable
}
}, availableOutputsChanged: { [weak self] availableOutputs, currentOutput in
Queue.mainQueue().async {
guard let strongSelf = self else {
return
}
strongSelf.audioOutputStateValue = (availableOutputs, currentOutput)
var signal: Signal<([AudioSessionOutput], AudioSessionOutput?), NoError> = .single((availableOutputs, currentOutput))
if !didReceiveAudioOutputs {
didReceiveAudioOutputs = true
if currentOutput == .speaker {
signal = .single((availableOutputs, .builtin))
|> then(
signal
|> delay(1.0, queue: Queue.mainQueue())
)
}
}
strongSelf.audioOutputStatePromise.set(signal)
}
})
self.audioSessionShouldBeActiveDisposable = (self.audioSessionShouldBeActive.get()
|> deliverOnMainQueue).start(next: { [weak self] value in
if let strongSelf = self {
if value {
if let audioSessionControl = strongSelf.audioSessionControl {
let audioSessionActive: Signal<Bool, NoError>
if let callKitIntegration = strongSelf.callKitIntegration {
audioSessionActive = callKitIntegration.audioSessionActive
|> filter { $0 }
|> timeout(2.0, queue: Queue.mainQueue(), alternate: Signal { subscriber in
if let strongSelf = self, let _ = strongSelf.audioSessionControl {
}
subscriber.putNext(true)
subscriber.putCompletion()
return EmptyDisposable
})
} else {
audioSessionControl.activate({ _ in })
audioSessionActive = .single(true)
}
strongSelf.audioSessionActive.set(audioSessionActive)
} else {
strongSelf.audioSessionActive.set(.single(false))
}
} else {
strongSelf.audioSessionActive.set(.single(false))
}
}
})
self.audioSessionActiveDisposable = (self.audioSessionActive.get()
|> deliverOnMainQueue).start(next: { [weak self] value in
if let strongSelf = self {
strongSelf.updateIsAudioSessionActive(value)
}
})
self.requestCall()
self.groupCallParticipantUpdatesDisposable = (self.account.stateManager.groupCallParticipantUpdates
|> deliverOnMainQueue).start(next: { [weak self] updates in
guard let strongSelf = self else {
return
}
if case let .estabilished(callInfo, _, _) = strongSelf.internalState {
var addedSsrc: [Int32] = []
for (callId, ssrc) in updates {
if callId == callInfo.id {
addedSsrc.append(ssrc)
}
}
if !addedSsrc.isEmpty {
strongSelf.callContext?.addSsrcs(ssrcs: addedSsrc)
}
}
})
}
deinit {
self.audioSessionShouldBeActiveDisposable?.dispose()
self.audioSessionActiveDisposable?.dispose()
self.sessionStateDisposable?.dispose()
self.audioSessionDisposable?.dispose()
self.requestDisposable.dispose()
self.groupCallParticipantUpdatesDisposable?.dispose()
}
private func updateSessionState(internalState: InternalState, audioSessionControl: ManagedAudioSessionControl?) {
let previousControl = self.audioSessionControl
self.audioSessionControl = audioSessionControl
let previousInternalState = self.internalState
self.internalState = internalState
if let audioSessionControl = audioSessionControl, previousControl == nil {
audioSessionControl.setOutputMode(.custom(self.currentAudioOutputValue))
audioSessionControl.setup(synchronous: true)
}
self.audioSessionShouldBeActive.set(true)
switch previousInternalState {
case .active:
break
default:
if case let .active(callInfo) = internalState {
let callContext = OngoingGroupCallContext()
self.callContext = callContext
self.requestDisposable.set((callContext.joinPayload
|> take(1)
|> deliverOnMainQueue).start(next: { [weak self] joinPayload in
guard let strongSelf = self else {
return
}
strongSelf.requestDisposable.set((joinGroupCall(
account: strongSelf.account,
callId: callInfo.id,
accessHash: callInfo.accessHash,
joinPayload: joinPayload
)
|> deliverOnMainQueue).start(next: { joinCallResult in
guard let strongSelf = self else {
return
}
if let clientParams = joinCallResult.callInfo.clientParams {
strongSelf.updateSessionState(internalState: .estabilished(joinCallResult.callInfo, clientParams, joinCallResult.ssrcs), audioSessionControl: strongSelf.audioSessionControl)
}
}))
}))
}
}
switch previousInternalState {
case .estabilished:
break
default:
if case let .estabilished(_, clientParams, ssrcs) = internalState {
self.callContext?.setJoinResponse(payload: clientParams, ssrcs: ssrcs)
}
}
}
private func updateIsAudioSessionActive(_ value: Bool) {
if self.isAudioSessionActive != value {
self.isAudioSessionActive = value
}
}
public func hangUp() -> Signal<Bool, NoError> {
return .single(true)
}
public func toggleIsMuted() {
self.setIsMuted(!self.isMutedValue)
}
public func setIsMuted(_ value: Bool) {
self.isMutedValue = value
self.isMutedPromise.set(self.isMutedValue)
self.callContext?.setIsMuted(self.isMutedValue)
}
public func setCurrentAudioOutput(_ output: AudioSessionOutput) {
guard self.currentAudioOutputValue != output else {
return
}
self.currentAudioOutputValue = output
self.audioOutputStatePromise.set(.single((self.audioOutputStateValue.0, output))
|> then(
.single(self.audioOutputStateValue)
|> delay(1.0, queue: Queue.mainQueue())
))
if let audioSessionControl = self.audioSessionControl {
audioSessionControl.setOutputMode(.custom(output))
}
}
private func requestCall() {
self.internalState = .requesting
enum CallError {
case generic
}
let account = self.account
let peerId = self.peerId
let currentCall = getCurrentGroupCall(account: account, peerId: peerId)
|> mapError { _ -> CallError in
return .generic
}
let currentOrRequestedCall = currentCall
|> mapToSignal { callInfo -> Signal<GroupCallInfo, CallError> in
if let callInfo = callInfo {
return .single(callInfo)
} else {
return createGroupCall(account: account, peerId: peerId)
|> mapError { _ -> CallError in
return .generic
}
}
}
let restartedCall = currentOrRequestedCall
|> mapToSignal { value -> Signal<GroupCallInfo, CallError> in
let stopped: Signal<GroupCallInfo, CallError> = stopGroupCall(account: account, callId: value.id, accessHash: value.accessHash)
|> mapError { _ -> CallError in
return .generic
}
|> map { _ -> GroupCallInfo in
}
return stopped
|> then(currentOrRequestedCall)
}
self.requestDisposable.set((currentOrRequestedCall
|> deliverOnMainQueue).start(next: { [weak self] value in
guard let strongSelf = self else {
return
}
strongSelf.updateSessionState(internalState: .active(value), audioSessionControl: strongSelf.audioSessionControl)
}))
}
}

View File

@ -110,6 +110,7 @@ enum AccountStateMutationOperation {
case UpdateChatListFilterOrder(order: [Int32])
case UpdateChatListFilter(id: Int32, filter: Api.DialogFilter?)
case UpdateReadThread(threadMessageId: MessageId, readMaxId: Int32, isIncoming: Bool, mainChannelMessage: MessageId?)
case UpdateGroupCallParticipant(id: Int64, accessHash: Int64, participant: Api.GroupCallParticipant)
}
struct HoleFromPreviousState {
@ -276,6 +277,10 @@ struct AccountMutableState {
self.addOperation(.UpdateReadThread(threadMessageId: threadMessageId, readMaxId: readMaxId, isIncoming: isIncoming, mainChannelMessage: mainChannelMessage))
}
mutating func updateGroupCallParticipant(id: Int64, accessHash: Int64, participant: Api.GroupCallParticipant) {
self.addOperation(.UpdateGroupCallParticipant(id: id, accessHash: accessHash, participant: participant))
}
mutating func readGroupFeedInbox(groupId: PeerGroupId, index: MessageIndex) {
self.addOperation(.ReadGroupFeedInbox(groupId, index))
}
@ -484,7 +489,7 @@ struct AccountMutableState {
mutating func addOperation(_ operation: AccountStateMutationOperation) {
switch operation {
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll/*, .UpdateMessageReactions*/, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter, .UpdateReadThread, .UpdateMessagesPinned:
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll/*, .UpdateMessageReactions*/, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter, .UpdateReadThread, .UpdateGroupCallParticipant, .UpdateMessagesPinned:
break
case let .AddMessages(messages, location):
for message in messages {
@ -602,6 +607,7 @@ struct AccountReplayedFinalState {
let updatedWebpages: [MediaId: TelegramMediaWebpage]
let updatedCalls: [Api.PhoneCall]
let addedCallSignalingData: [(Int64, Data)]
let updatedGroupCallParticipants: [(Int64, Int32)]
let updatedPeersNearby: [PeerNearby]?
let isContactUpdates: [(PeerId, Bool)]
let delayNotificatonsUntil: Int32?
@ -617,6 +623,7 @@ struct AccountFinalStateEvents {
let updatedWebpages: [MediaId: TelegramMediaWebpage]
let updatedCalls: [Api.PhoneCall]
let addedCallSignalingData: [(Int64, Data)]
let updatedGroupCallParticipants: [(Int64, Int32)]
let updatedPeersNearby: [PeerNearby]?
let isContactUpdates: [(PeerId, Bool)]
let displayAlerts: [(text: String, isDropAuth: Bool)]
@ -629,10 +636,10 @@ struct AccountFinalStateEvents {
let updatedOutgoingThreadReadStates: [MessageId: MessageId.Id]
var isEmpty: Bool {
return self.addedIncomingMessageIds.isEmpty && self.wasScheduledMessageIds.isEmpty && self.deletedMessageIds.isEmpty && self.updatedTypingActivities.isEmpty && self.updatedWebpages.isEmpty && self.updatedCalls.isEmpty && self.addedCallSignalingData.isEmpty && self.updatedPeersNearby?.isEmpty ?? true && self.isContactUpdates.isEmpty && self.displayAlerts.isEmpty && self.delayNotificatonsUntil == nil && self.updatedMaxMessageId == nil && self.updatedQts == nil && self.externallyUpdatedPeerId.isEmpty && !authorizationListUpdated && self.updatedIncomingThreadReadStates.isEmpty && self.updatedOutgoingThreadReadStates.isEmpty
return self.addedIncomingMessageIds.isEmpty && self.wasScheduledMessageIds.isEmpty && self.deletedMessageIds.isEmpty && self.updatedTypingActivities.isEmpty && self.updatedWebpages.isEmpty && self.updatedCalls.isEmpty && self.addedCallSignalingData.isEmpty && self.updatedGroupCallParticipants.isEmpty && self.updatedPeersNearby?.isEmpty ?? true && self.isContactUpdates.isEmpty && self.displayAlerts.isEmpty && self.delayNotificatonsUntil == nil && self.updatedMaxMessageId == nil && self.updatedQts == nil && self.externallyUpdatedPeerId.isEmpty && !authorizationListUpdated && self.updatedIncomingThreadReadStates.isEmpty && self.updatedOutgoingThreadReadStates.isEmpty
}
init(addedIncomingMessageIds: [MessageId] = [], wasScheduledMessageIds: [MessageId] = [], deletedMessageIds: [DeletedMessageId] = [], updatedTypingActivities: [PeerActivitySpace: [PeerId: PeerInputActivity?]] = [:], updatedWebpages: [MediaId: TelegramMediaWebpage] = [:], updatedCalls: [Api.PhoneCall] = [], addedCallSignalingData: [(Int64, Data)] = [], updatedPeersNearby: [PeerNearby]? = nil, isContactUpdates: [(PeerId, Bool)] = [], displayAlerts: [(text: String, isDropAuth: Bool)] = [], delayNotificatonsUntil: Int32? = nil, updatedMaxMessageId: Int32? = nil, updatedQts: Int32? = nil, externallyUpdatedPeerId: Set<PeerId> = Set(), authorizationListUpdated: Bool = false, updatedIncomingThreadReadStates: [MessageId: MessageId.Id] = [:], updatedOutgoingThreadReadStates: [MessageId: MessageId.Id] = [:]) {
init(addedIncomingMessageIds: [MessageId] = [], wasScheduledMessageIds: [MessageId] = [], deletedMessageIds: [DeletedMessageId] = [], updatedTypingActivities: [PeerActivitySpace: [PeerId: PeerInputActivity?]] = [:], updatedWebpages: [MediaId: TelegramMediaWebpage] = [:], updatedCalls: [Api.PhoneCall] = [], addedCallSignalingData: [(Int64, Data)] = [], updatedGroupCallParticipants: [(Int64, Int32)] = [], updatedPeersNearby: [PeerNearby]? = nil, isContactUpdates: [(PeerId, Bool)] = [], displayAlerts: [(text: String, isDropAuth: Bool)] = [], delayNotificatonsUntil: Int32? = nil, updatedMaxMessageId: Int32? = nil, updatedQts: Int32? = nil, externallyUpdatedPeerId: Set<PeerId> = Set(), authorizationListUpdated: Bool = false, updatedIncomingThreadReadStates: [MessageId: MessageId.Id] = [:], updatedOutgoingThreadReadStates: [MessageId: MessageId.Id] = [:]) {
self.addedIncomingMessageIds = addedIncomingMessageIds
self.wasScheduledMessageIds = wasScheduledMessageIds
self.deletedMessageIds = deletedMessageIds
@ -640,6 +647,7 @@ struct AccountFinalStateEvents {
self.updatedWebpages = updatedWebpages
self.updatedCalls = updatedCalls
self.addedCallSignalingData = addedCallSignalingData
self.updatedGroupCallParticipants = updatedGroupCallParticipants
self.updatedPeersNearby = updatedPeersNearby
self.isContactUpdates = isContactUpdates
self.displayAlerts = displayAlerts
@ -660,6 +668,7 @@ struct AccountFinalStateEvents {
self.updatedWebpages = state.updatedWebpages
self.updatedCalls = state.updatedCalls
self.addedCallSignalingData = state.addedCallSignalingData
self.updatedGroupCallParticipants = state.updatedGroupCallParticipants
self.updatedPeersNearby = state.updatedPeersNearby
self.isContactUpdates = state.isContactUpdates
self.displayAlerts = state.state.state.displayAlerts
@ -695,6 +704,6 @@ struct AccountFinalStateEvents {
let externallyUpdatedPeerId = self.externallyUpdatedPeerId.union(other.externallyUpdatedPeerId)
let authorizationListUpdated = self.authorizationListUpdated || other.authorizationListUpdated
return AccountFinalStateEvents(addedIncomingMessageIds: self.addedIncomingMessageIds + other.addedIncomingMessageIds, wasScheduledMessageIds: self.wasScheduledMessageIds + other.wasScheduledMessageIds, deletedMessageIds: self.deletedMessageIds + other.deletedMessageIds, updatedTypingActivities: self.updatedTypingActivities, updatedWebpages: self.updatedWebpages, updatedCalls: self.updatedCalls + other.updatedCalls, addedCallSignalingData: self.addedCallSignalingData + other.addedCallSignalingData, isContactUpdates: self.isContactUpdates + other.isContactUpdates, displayAlerts: self.displayAlerts + other.displayAlerts, delayNotificatonsUntil: delayNotificatonsUntil, updatedMaxMessageId: updatedMaxMessageId, updatedQts: updatedQts, externallyUpdatedPeerId: externallyUpdatedPeerId, authorizationListUpdated: authorizationListUpdated, updatedIncomingThreadReadStates: self.updatedIncomingThreadReadStates.merging(other.updatedIncomingThreadReadStates, uniquingKeysWith: { lhs, _ in lhs }))
return AccountFinalStateEvents(addedIncomingMessageIds: self.addedIncomingMessageIds + other.addedIncomingMessageIds, wasScheduledMessageIds: self.wasScheduledMessageIds + other.wasScheduledMessageIds, deletedMessageIds: self.deletedMessageIds + other.deletedMessageIds, updatedTypingActivities: self.updatedTypingActivities, updatedWebpages: self.updatedWebpages, updatedCalls: self.updatedCalls + other.updatedCalls, addedCallSignalingData: self.addedCallSignalingData + other.addedCallSignalingData, updatedGroupCallParticipants: self.updatedGroupCallParticipants + other.updatedGroupCallParticipants, isContactUpdates: self.isContactUpdates + other.isContactUpdates, displayAlerts: self.displayAlerts + other.displayAlerts, delayNotificatonsUntil: delayNotificatonsUntil, updatedMaxMessageId: updatedMaxMessageId, updatedQts: updatedQts, externallyUpdatedPeerId: externallyUpdatedPeerId, authorizationListUpdated: authorizationListUpdated, updatedIncomingThreadReadStates: self.updatedIncomingThreadReadStates.merging(other.updatedIncomingThreadReadStates, uniquingKeysWith: { lhs, _ in lhs }))
}
}

View File

@ -1306,6 +1306,11 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo
updatedState.addUpdateCall(phoneCall)
case let .updatePhoneCallSignalingData(phoneCallId, data):
updatedState.addCallSignalingData(callId: phoneCallId, data: data.makeData())
case let .updateGroupCallParticipant(call, participant):
switch call {
case let .inputGroupCall(id, accessHash):
updatedState.updateGroupCallParticipant(id: id, accessHash: accessHash, participant: participant)
}
case let .updateLangPackTooLong(langCode):
updatedState.updateLangPack(langCode: langCode, difference: nil)
case let .updateLangPack(difference):
@ -2109,7 +2114,7 @@ private func optimizedOperations(_ operations: [AccountStateMutationOperation])
var currentAddScheduledMessages: OptimizeAddMessagesState?
for operation in operations {
switch operation {
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll/*, .UpdateMessageReactions*/, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .ResetIncomingReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder, .UpdateReadThread, .UpdateMessagesPinned:
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll/*, .UpdateMessageReactions*/, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .ResetIncomingReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder, .UpdateReadThread, .UpdateMessagesPinned, .UpdateGroupCallParticipant:
if let currentAddMessages = currentAddMessages, !currentAddMessages.messages.isEmpty {
result.append(.AddMessages(currentAddMessages.messages, currentAddMessages.location))
}
@ -2196,6 +2201,7 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
var updatedWebpages: [MediaId: TelegramMediaWebpage] = [:]
var updatedCalls: [Api.PhoneCall] = []
var addedCallSignalingData: [(Int64, Data)] = []
var updatedGroupCallParticipants: [(Int64, Int32)] = []
var updatedPeersNearby: [PeerNearby]?
var isContactUpdates: [(PeerId, Bool)] = []
var stickerPackOperations: [AccountStateUpdateStickerPacksOperation] = []
@ -2925,6 +2931,23 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
updatedCalls.append(call)
case let .AddCallSignalingData(callId, data):
addedCallSignalingData.append((callId, data))
case let .UpdateGroupCallParticipant(callId, _, participant):
var ssrc: Int32?
switch participant {
case let .groupCallParticipantAdmin(_, source):
ssrc = source
case let .groupCallParticipant(_, _, _, source):
ssrc = source
case .groupCallParticipantLeft:
break
case .groupCallParticipantKicked:
break
case .groupCallParticipantInvited:
break
}
if let ssrc = ssrc {
updatedGroupCallParticipants.append((callId, ssrc))
}
case let .UpdateLangPack(langCode, difference):
if let difference = difference {
if langPackDifferences[langCode] == nil {
@ -3347,5 +3370,5 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
requestChatListFiltersSync(transaction: transaction)
}
return AccountReplayedFinalState(state: finalState, addedIncomingMessageIds: addedIncomingMessageIds, wasScheduledMessageIds: wasScheduledMessageIds, addedSecretMessageIds: addedSecretMessageIds, deletedMessageIds: deletedMessageIds, updatedTypingActivities: updatedTypingActivities, updatedWebpages: updatedWebpages, updatedCalls: updatedCalls, addedCallSignalingData: addedCallSignalingData, updatedPeersNearby: updatedPeersNearby, isContactUpdates: isContactUpdates, delayNotificatonsUntil: delayNotificatonsUntil, updatedIncomingThreadReadStates: updatedIncomingThreadReadStates, updatedOutgoingThreadReadStates: updatedOutgoingThreadReadStates)
return AccountReplayedFinalState(state: finalState, addedIncomingMessageIds: addedIncomingMessageIds, wasScheduledMessageIds: wasScheduledMessageIds, addedSecretMessageIds: addedSecretMessageIds, deletedMessageIds: deletedMessageIds, updatedTypingActivities: updatedTypingActivities, updatedWebpages: updatedWebpages, updatedCalls: updatedCalls, addedCallSignalingData: addedCallSignalingData, updatedGroupCallParticipants: updatedGroupCallParticipants, updatedPeersNearby: updatedPeersNearby, isContactUpdates: isContactUpdates, delayNotificatonsUntil: delayNotificatonsUntil, updatedIncomingThreadReadStates: updatedIncomingThreadReadStates, updatedOutgoingThreadReadStates: updatedOutgoingThreadReadStates)
}

View File

@ -148,6 +148,11 @@ public final class AccountStateManager {
return self.threadReadStateUpdatesPipe.signal()
}
private let groupCallParticipantUpdatesPipe = ValuePipe<[(Int64, Int32)]>()
public var groupCallParticipantUpdates: Signal<[(Int64, Int32)], NoError> {
return self.groupCallParticipantUpdatesPipe.signal()
}
private let deletedMessagesPipe = ValuePipe<[DeletedMessageId]>()
public var deletedMessages: Signal<[DeletedMessageId], NoError> {
return self.deletedMessagesPipe.signal()
@ -673,6 +678,9 @@ public final class AccountStateManager {
strongSelf.callSessionManager.addCallSignalingData(id: id, data: data)
}
}
if !events.updatedGroupCallParticipants.isEmpty {
strongSelf.groupCallParticipantUpdatesPipe.putNext(events.updatedGroupCallParticipants)
}
if !events.updatedIncomingThreadReadStates.isEmpty || !events.updatedOutgoingThreadReadStates.isEmpty {
strongSelf.threadReadStateUpdatesPipe.putNext((events.updatedIncomingThreadReadStates, events.updatedOutgoingThreadReadStates))
}

View File

@ -0,0 +1,287 @@
import Postbox
import SwiftSignalKit
import TelegramApi
import MtProtoKit
import SyncCore
public struct GroupCallInfo: Equatable {
public var id: Int64
public var accessHash: Int64
public var peerId: PeerId?
public var clientParams: String?
}
private extension GroupCallInfo {
init?(_ call: Api.GroupCall) {
switch call {
case let .groupCallPrivate(_, id, accessHash, channelId, _, _):
self.init(
id: id,
accessHash: accessHash,
peerId: channelId.flatMap { PeerId(namespace: Namespaces.Peer.CloudChannel, id: $0) },
clientParams: nil
)
case let .groupCall(_, id, accessHash, channelId, _, _, params):
var clientParams: String?
if let params = params {
switch params {
case let .dataJSON(data):
clientParams = data
}
}
self.init(
id: id,
accessHash: accessHash,
peerId: channelId.flatMap { PeerId(namespace: Namespaces.Peer.CloudChannel, id: $0) },
clientParams: clientParams
)
case .groupCallDiscarded:
return nil
}
}
}
public enum GetCurrentGroupCallError {
case generic
}
public func getCurrentGroupCall(account: Account, peerId: PeerId) -> Signal<GroupCallInfo?, GetCurrentGroupCallError> {
return account.postbox.transaction { transaction -> Api.InputChannel? in
transaction.getPeer(peerId).flatMap(apiInputChannel)
}
|> castError(GetCurrentGroupCallError.self)
|> mapToSignal { inputPeer -> Signal<MessageId?, GetCurrentGroupCallError> in
guard let inputPeer = inputPeer else {
return .fail(.generic)
}
return account.network.request(Api.functions.channels.getFullChannel(channel: inputPeer))
|> mapError { _ -> GetCurrentGroupCallError in
return .generic
}
|> mapToSignal { result -> Signal<MessageId?, GetCurrentGroupCallError> in
switch result {
case let .chatFull(fullChat, _, _):
switch fullChat {
case let .channelFull(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, callMsgId):
return .single(callMsgId.flatMap { callMsgId in
MessageId(peerId: peerId, namespace: Namespaces.Peer.CloudChannel, id: callMsgId)
})
default:
return .single(nil)
}
default:
return .single(nil)
}
}
}
|> mapToSignal { messageId -> Signal<GroupCallInfo?, GetCurrentGroupCallError> in
guard let messageId = messageId else {
return .single(nil)
}
return account.postbox.transaction { transaction -> Api.InputChannel? in
return transaction.getPeer(peerId).flatMap(apiInputChannel)
}
|> castError(GetCurrentGroupCallError.self)
|> mapToSignal { inputPeer -> Signal<GroupCallInfo?, GetCurrentGroupCallError> in
guard let inputPeer = inputPeer else {
return .fail(.generic)
}
return account.network.request(Api.functions.channels.getMessages(channel: inputPeer, id: [.inputMessageID(id: messageId.id)]))
|> mapError { _ -> GetCurrentGroupCallError in
return .generic
}
|> mapToSignal { result -> Signal<GroupCallInfo?, GetCurrentGroupCallError> in
let messages: [Api.Message]
let chats: [Api.Chat]
let users: [Api.User]
switch result {
case let .messages(apiMessages, apiChats, apiUsers):
messages = apiMessages
chats = apiChats
users = apiUsers
case let .messagesSlice(_, _, _, _, messages: apiMessages, chats: apiChats, users: apiUsers):
messages = apiMessages
chats = apiChats
users = apiUsers
case let .channelMessages(_, _, _, _, apiMessages, apiChats, apiUsers):
messages = apiMessages
chats = apiChats
users = apiUsers
case .messagesNotModified:
return .fail(.generic)
}
guard let apiMessage = messages.first else {
return .single(nil)
}
guard let message = StoreMessage(apiMessage: apiMessage) else {
return .fail(.generic)
}
var maybeInputCall: Api.InputGroupCall?
loop: for media in message.media {
if let action = media as? TelegramMediaAction {
switch action.action {
case let .groupPhoneCall(callId, accessHash, _):
maybeInputCall = .inputGroupCall(id: callId, accessHash: accessHash)
break loop
default:
break
}
}
}
guard let inputCall = maybeInputCall else {
return .fail(.generic)
}
return account.network.request(Api.functions.phone.getGroupCall(call: inputCall))
|> mapError { _ -> GetCurrentGroupCallError in
return .generic
}
|> mapToSignal { result -> Signal<GroupCallInfo?, GetCurrentGroupCallError> in
switch result {
case let .groupCall(call, participants, chats, users):
return account.postbox.transaction { transaction -> GroupCallInfo? in
return GroupCallInfo(call)
}
|> mapError { _ -> GetCurrentGroupCallError in
return .generic
}
}
}
}
}
}
}
public enum CreateGroupCallError {
case generic
}
public func createGroupCall(account: Account, peerId: PeerId) -> Signal<GroupCallInfo, CreateGroupCallError> {
return account.postbox.transaction { transaction -> Api.InputChannel? in
return transaction.getPeer(peerId).flatMap(apiInputChannel)
}
|> castError(CreateGroupCallError.self)
|> mapToSignal { inputPeer -> Signal<GroupCallInfo, CreateGroupCallError> in
guard let inputPeer = inputPeer else {
return .fail(.generic)
}
return account.network.request(Api.functions.phone.createGroupCall(flags: 0, channel: inputPeer, randomId: Int32.random(in: Int32.min ... Int32.max)))
|> mapError { _ -> CreateGroupCallError in
return .generic
}
|> mapToSignal { result -> Signal<GroupCallInfo, CreateGroupCallError> in
account.stateManager.addUpdates(result)
var parsedCall: GroupCallInfo?
loop: for update in result.allUpdates {
switch update {
case let .updateGroupCall(call):
parsedCall = GroupCallInfo(call)
break loop
default:
break
}
}
if let parsedCall = parsedCall {
return .single(parsedCall)
} else {
return .fail(.generic)
}
}
}
}
public enum JoinGroupCallError {
case generic
}
public struct JoinGroupCallResult {
public var callInfo: GroupCallInfo
public var ssrcs: [Int32]
}
public func joinGroupCall(account: Account, callId: Int64, accessHash: Int64, joinPayload: String) -> Signal<JoinGroupCallResult, JoinGroupCallError> {
return account.network.request(Api.functions.phone.joinGroupCall(call: .inputGroupCall(id: callId, accessHash: accessHash), params: .dataJSON(data: joinPayload)))
|> mapError { _ -> JoinGroupCallError in
return .generic
}
|> mapToSignal { updates -> Signal<JoinGroupCallResult, JoinGroupCallError> in
return account.network.request(Api.functions.phone.getGroupCall(call: .inputGroupCall(id: callId, accessHash: accessHash)))
|> mapError { _ -> JoinGroupCallError in
return .generic
}
|> mapToSignal { result -> Signal<JoinGroupCallResult, JoinGroupCallError> in
account.stateManager.addUpdates(updates)
var maybeParsedCall: GroupCallInfo?
loop: for update in updates.allUpdates {
switch update {
case let .updateGroupCall(call):
maybeParsedCall = GroupCallInfo(call)
break loop
default:
break
}
}
guard let parsedCall = maybeParsedCall else {
return .fail(.generic)
}
switch result {
case let .groupCall(call, participants, chats, users):
guard let _ = GroupCallInfo(call) else {
return .fail(.generic)
}
var ssrcs: [Int32] = []
for participant in participants {
var ssrc: Int32?
switch participant {
case let .groupCallParticipantAdmin(_, source):
ssrc = source
case let .groupCallParticipant(_, _, _, source):
ssrc = source
case .groupCallParticipantLeft:
break
case .groupCallParticipantKicked:
break
case .groupCallParticipantInvited:
break
}
if let ssrc = ssrc {
ssrcs.append(ssrc)
}
}
return account.postbox.transaction { transaction -> JoinGroupCallResult in
return JoinGroupCallResult(
callInfo: parsedCall,
ssrcs: ssrcs
)
}
|> castError(JoinGroupCallError.self)
}
}
}
}
public enum StopGroupCallError {
case generic
}
public func stopGroupCall(account: Account, callId: Int64, accessHash: Int64) -> Signal<Never, StopGroupCallError> {
return account.network.request(Api.functions.phone.discardGroupCall(call: .inputGroupCall(id: callId, accessHash: accessHash)))
|> mapError { _ -> StopGroupCallError in
return .generic
}
|> mapToSignal { result -> Signal<Never, StopGroupCallError> in
account.stateManager.addUpdates(result)
return .complete()
}
}

View File

@ -210,7 +210,7 @@ public class BoxedMessage: NSObject {
public class Serialization: NSObject, MTSerialization {
public func currentLayer() -> UInt {
return 121
return 122
}
public func parseMessage(_ data: Data!) -> Any! {

View File

@ -188,7 +188,7 @@ func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] {
}
switch action {
case .messageActionChannelCreate, .messageActionChatDeletePhoto, .messageActionChatEditPhoto, .messageActionChatEditTitle, .messageActionEmpty, .messageActionPinMessage, .messageActionHistoryClear, .messageActionGameScore, .messageActionPaymentSent, .messageActionPaymentSentMe, .messageActionPhoneCall, .messageActionScreenshotTaken, .messageActionCustomAction, .messageActionBotAllowed, .messageActionSecureValuesSent, .messageActionSecureValuesSentMe, .messageActionContactSignUp:
case .messageActionChannelCreate, .messageActionChatDeletePhoto, .messageActionChatEditPhoto, .messageActionChatEditTitle, .messageActionEmpty, .messageActionPinMessage, .messageActionHistoryClear, .messageActionGameScore, .messageActionPaymentSent, .messageActionPaymentSentMe, .messageActionPhoneCall, .messageActionScreenshotTaken, .messageActionCustomAction, .messageActionBotAllowed, .messageActionSecureValuesSent, .messageActionSecureValuesSentMe, .messageActionContactSignUp, .messageActionGroupCall:
break
case let .messageActionChannelMigrateFrom(_, chatId):
result.append(PeerId(namespace: Namespaces.Peer.CloudGroup, id: chatId))

View File

@ -59,6 +59,11 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe
return TelegramMediaAction(action: .peerJoined)
case let .messageActionGeoProximityReached(fromId, toId, distance):
return TelegramMediaAction(action: .geoProximityReached(from: fromId.peerId, to: toId.peerId, distance: distance))
case let .messageActionGroupCall(_, call, duration):
switch call {
case let .inputGroupCall(id, accessHash):
return TelegramMediaAction(action: .groupPhoneCall(callId: id, accessHash: accessHash, duration: duration))
}
}
}

View File

@ -346,7 +346,7 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
}
switch fullChat {
case let .channelFull(flags, _, about, participantsCount, adminsCount, kickedCount, bannedCount, _, _, _, _, chatPhoto, _, apiExportedInvite, apiBotInfos, migratedFromChatId, migratedFromMaxId, pinnedMsgId, stickerSet, minAvailableMsgId, folderId, linkedChatId, location, slowmodeSeconds, slowmodeNextSendDate, statsDc, pts):
case let .channelFull(flags, _, about, participantsCount, adminsCount, kickedCount, bannedCount, _, _, _, _, chatPhoto, _, apiExportedInvite, apiBotInfos, migratedFromChatId, migratedFromMaxId, pinnedMsgId, stickerSet, minAvailableMsgId, folderId, linkedChatId, location, slowmodeSeconds, slowmodeNextSendDate, statsDc, pts, callMsgId):
var channelFlags = CachedChannelFlags()
if (flags & (1 << 3)) != 0 {
channelFlags.insert(.canDisplayParticipants)

View File

@ -338,6 +338,21 @@ extension Api.Update {
}
}
extension Api.Updates {
var allUpdates: [Api.Update] {
switch self {
case let .updates(updates, _, _, _, _):
return updates
case let .updatesCombined(updates, _, _, _, _, _):
return updates
case let .updateShort(update, _):
return [update]
default:
return []
}
}
}
extension Api.Updates {
var rawMessageIds: [Int32] {
switch self {

View File

@ -404,6 +404,15 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
}
}
attributedString = NSAttributedString(string: titleString, font: titleFont, textColor: primaryTextColor)
case let .groupPhoneCall(_, _, duration):
//TODO:localize
let titleString: String
if let duration = duration {
titleString = "Group Call \(duration)s"
} else {
titleString = "Group Call"
}
attributedString = NSAttributedString(string: titleString, font: titleFont, textColor: primaryTextColor)
case let .customText(text, entities):
attributedString = stringWithAppliedEntities(text, entities: entities, baseColor: primaryTextColor, linkColor: primaryTextColor, baseFont: titleFont, linkFont: titleBoldFont, boldFont: titleBoldFont, italicFont: titleFont, boldItalicFont: titleBoldFont, fixedFont: titleFont, blockQuoteFont: titleFont, underlineLinks: false)
case let .botDomainAccessGranted(domain):

View File

@ -949,6 +949,7 @@ func peerInfoHeaderButtons(peer: Peer?, cachedData: CachedPeerData?, isOpenedFro
displayLeave = false
if channel.flags.contains(.isCreator) || channel.hasPermission(.inviteMembers) {
result.append(.addMember)
result.append(.call)
}
}
switch channel.participationStatus {

View File

@ -3163,6 +3163,12 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}
private func requestCall(isVideo: Bool) {
if let peer = self.data?.peer as? TelegramChannel {
self.context.sharedContext.callManager?.requestOrJoinGroupCall(context: self.context, peerId: peer.id)
return
}
guard let peer = self.data?.peer as? TelegramUser, let cachedUserData = self.data?.cachedData as? CachedUserData else {
return
}

View File

@ -732,7 +732,7 @@ private extension ConferenceDescription {
let videoMid: String?
if stream.isMain {
audioMid = "0"
if let videoSsrc = stream.videoSsrc {
if let _ = stream.videoSsrc {
videoMid = "1"
} else {
videoMid = nil
@ -740,7 +740,7 @@ private extension ConferenceDescription {
} else {
audioMid = "audio\(stream.audioSsrc)"
if let videoSsrc = stream.videoSsrc {
videoMid = "video\(stream.videoSsrc)"
videoMid = "video\(videoSsrc)"
} else {
videoMid = nil
}
@ -1590,17 +1590,6 @@ public final class GroupCallContext {
strongSelf.relaySdpAnswer(sdpAnswer: offerSdp)
}
})
/*guard let offer = conference.offerSdp(sessionId: strongSelf.sessionId, bundleId: bundleId, bridgeHost: strongSelf.colibriHost, transport: transport, currentState: strongSelf.currentOfferState) else {
return
}
strongSelf.currentOfferState = offer.state
strongSelf.memberCount.set(offer.state.items.filter({ !$0.isRemoved }).count)
for sdp in offer.sdpList {
strongSelf.context.setOfferSdp(sdp, isPartial: false)
}*/
}))
}
@ -1698,8 +1687,6 @@ public final class GroupCallContext {
if let offer = conference.offerSdp(sessionId: strongSelf.sessionId, bundleId: localBundleId, bridgeHost: strongSelf.colibriHost, transport: localTransport, currentState: strongSelf.currentOfferState, isAnswer: false) {
strongSelf.currentOfferState = offer.state
let queue = strongSelf.queue
strongSelf.memberCount.set(offer.state.items.filter({ !$0.isRemoved }).count)
for sdp in offer.sdpList {
@ -1837,6 +1824,14 @@ public final class GroupCallContext {
self.context.setIsMuted(self.isMutedValue)
}
func setIsMuted(_ isMuted: Bool) {
if self.isMutedValue != isMuted {
self.isMutedValue = isMuted
self.isMuted.set(self.isMutedValue)
self.context.setIsMuted(self.isMutedValue)
}
}
func makeIncomingVideoView(id: String, completion: @escaping (OngoingCallContextPresentationCallVideoView?) -> Void) {
self.context.makeIncomingVideoView(withStreamId: id, completion: { view in
if let view = view {
@ -1929,6 +1924,12 @@ public final class GroupCallContext {
}
}
public func setIsMuted(_ isMuted: Bool) {
self.impl.with { impl in
impl.setIsMuted(isMuted)
}
}
public func makeIncomingVideoView(id: String, completion: @escaping (OngoingCallContextPresentationCallVideoView?) -> Void) {
self.impl.with { impl in
impl.makeIncomingVideoView(id: id, completion: completion)
@ -1936,3 +1937,527 @@ public final class GroupCallContext {
}
}
private struct ParsedJoinPayload {
var payload: String
var audioSsrc: UInt32
}
private func parseSdpIntoJoinPayload(sdp: String) -> ParsedJoinPayload? {
let lines = sdp.components(separatedBy: "\n")
var videoLines: [String] = []
var audioLines: [String] = []
var isAudioLine = false
var isVideoLine = false
for line in lines {
if line.hasPrefix("m=audio") {
isAudioLine = true
isVideoLine = false
} else if line.hasPrefix("m=video") {
isVideoLine = true
isAudioLine = false
}
if isAudioLine {
audioLines.append(line)
} else if isVideoLine {
videoLines.append(line)
}
}
func getLines(prefix: String) -> [String] {
var result: [String] = []
for line in lines {
if line.hasPrefix(prefix) {
var cleanLine = String(line[line.index(line.startIndex, offsetBy: prefix.count)...])
if cleanLine.hasSuffix("\r") {
cleanLine.removeLast()
}
result.append(cleanLine)
}
}
return result
}
func getLines(prefix: String, isAudio: Bool) -> [String] {
var result: [String] = []
for line in (isAudio ? audioLines : videoLines) {
if line.hasPrefix(prefix) {
var cleanLine = String(line[line.index(line.startIndex, offsetBy: prefix.count)...])
if cleanLine.hasSuffix("\r") {
cleanLine.removeLast()
}
result.append(cleanLine)
}
}
return result
}
var audioSources: [Int] = []
for line in getLines(prefix: "a=ssrc:", isAudio: true) {
let scanner = Scanner(string: line)
if #available(iOS 13.0, *) {
if let ssrc = scanner.scanInt() {
if !audioSources.contains(ssrc) {
audioSources.append(ssrc)
}
}
}
}
guard let ssrc = audioSources.first else {
return nil
}
guard let ufrag = getLines(prefix: "a=ice-ufrag:").first else {
return nil
}
guard let pwd = getLines(prefix: "a=ice-pwd:").first else {
return nil
}
var resultPayload: [String: Any] = [:]
var fingerprints: [[String: Any]] = []
for line in getLines(prefix: "a=fingerprint:") {
let components = line.components(separatedBy: " ")
if components.count != 2 {
continue
}
fingerprints.append([
"hash": components[0],
"fingerprint": components[1],
"setup": "active"
])
}
resultPayload["fingerprints"] = fingerprints
resultPayload["ufrag"] = ufrag
resultPayload["pwd"] = pwd
resultPayload["ssrc"] = ssrc
guard let payloadData = try? JSONSerialization.data(withJSONObject: resultPayload, options: []) else {
return nil
}
guard let payloadString = String(data: payloadData, encoding: .utf8) else {
return nil
}
return ParsedJoinPayload(
payload: payloadString,
audioSsrc: UInt32(ssrc)
)
}
private func parseJoinResponseIntoSdp(sessionId: UInt32, mainStreamAudioSsrc: UInt32, payload: String, isAnswer: Bool, otherSsrcs: [UInt32]) -> String? {
guard let payloadData = payload.data(using: .utf8) else {
return nil
}
guard let jsonPayload = try? JSONSerialization.jsonObject(with: payloadData, options: []) as? [String: Any] else {
return nil
}
guard let transport = jsonPayload["transport"] as? [String: Any] else {
return nil
}
guard let pwd = transport["pwd"] as? String else {
return nil
}
guard let ufrag = transport["ufrag"] as? String else {
return nil
}
struct ParsedFingerprint {
var hashValue: String
var fingerprint: String
var setup: String
}
var fingerprints: [ParsedFingerprint] = []
guard let fingerprintsValue = transport["fingerprints"] as? [[String: Any]] else {
return nil
}
for fingerprintValue in fingerprintsValue {
guard let hashValue = fingerprintValue["hash"] as? String else {
continue
}
guard let fingerprint = fingerprintValue["fingerprint"] as? String else {
continue
}
guard let setup = fingerprintValue["setup"] as? String else {
continue
}
fingerprints.append(ParsedFingerprint(
hashValue: hashValue,
fingerprint: fingerprint,
setup: setup
))
}
struct ParsedCandidate {
var port: String
var `protocol`: String
var network: String
var generation: String
var id: String
var component: String
var foundation: String
var priority: String
var ip: String
var type: String
var tcpType: String?
var relAddr: String?
var relPort: String?
}
var candidates: [ParsedCandidate] = []
guard let candidatesValue = transport["candidates"] as? [[String: Any]] else {
return nil
}
for candidateValue in candidatesValue {
guard let port = candidateValue["port"] as? String else {
continue
}
guard let `protocol` = candidateValue["protocol"] as? String else {
continue
}
guard let network = candidateValue["network"] as? String else {
continue
}
guard let generation = candidateValue["generation"] as? String else {
continue
}
guard let id = candidateValue["id"] as? String else {
continue
}
guard let component = candidateValue["component"] as? String else {
continue
}
guard let foundation = candidateValue["foundation"] as? String else {
continue
}
guard let priority = candidateValue["priority"] as? String else {
continue
}
guard let ip = candidateValue["ip"] as? String else {
continue
}
guard let type = candidateValue["type"] as? String else {
continue
}
let tcpType = candidateValue["tcptype"] as? String
let relAddr = candidateValue["rel-addr"] as? String
let relPort = candidateValue["rel-port"] as? String
candidates.append(ParsedCandidate(
port: port,
protocol: `protocol`,
network: network,
generation: generation,
id: id,
component: component,
foundation: foundation,
priority: priority,
ip: ip,
type: type,
tcpType: tcpType,
relAddr: relAddr,
relPort: relPort
))
}
struct StreamSpec {
var isMain: Bool
var audioSsrc: Int
var isRemoved: Bool
}
func createSdp(sessionId: UInt32, bundleStreams: [StreamSpec]) -> String {
var sdp = ""
func appendSdp(_ string: String) {
if !sdp.isEmpty {
sdp.append("\n")
}
sdp.append(string)
}
appendSdp("v=0")
appendSdp("o=- \(sessionId) 2 IN IP4 0.0.0.0")
appendSdp("s=-")
appendSdp("t=0 0")
var bundleString = "a=group:BUNDLE"
for stream in bundleStreams {
bundleString.append(" ")
let audioMid: String
if stream.isMain {
audioMid = "0"
} else {
audioMid = "audio\(stream.audioSsrc)"
}
bundleString.append("\(audioMid)")
}
appendSdp(bundleString)
appendSdp("a=ice-lite")
for stream in bundleStreams {
let audioMid: String
if stream.isMain {
audioMid = "0"
} else {
audioMid = "audio\(stream.audioSsrc)"
}
appendSdp("m=audio \(stream.isMain ? "1" : "0") RTP/SAVPF 111 126")
if stream.isMain {
appendSdp("c=IN IP4 0.0.0.0")
}
appendSdp("a=mid:\(audioMid)")
if stream.isRemoved {
appendSdp("a=inactive")
} else {
if stream.isMain {
appendSdp("a=ice-ufrag:\(ufrag)")
appendSdp("a=ice-pwd:\(pwd)")
for fingerprint in fingerprints {
appendSdp("a=fingerprint:\(fingerprint.hashValue) \(fingerprint.fingerprint)")
appendSdp("a=setup:passive")
}
for candidate in candidates {
var candidateString = "a=candidate:"
candidateString.append("\(candidate.foundation) ")
candidateString.append("\(candidate.component) ")
var protocolValue = candidate.protocol
if protocolValue == "ssltcp" {
protocolValue = "tcp"
}
candidateString.append("\(protocolValue) ")
candidateString.append("\(candidate.priority) ")
let ip = candidate.ip
candidateString.append("\(ip) ")
candidateString.append("\(candidate.port) ")
candidateString.append("typ \(candidate.type) ")
switch candidate.type {
case "srflx", "prflx", "relay":
if let relAddr = candidate.relAddr, let relPort = candidate.relPort {
candidateString.append("raddr \(relAddr) rport \(relPort) ")
}
break
default:
break
}
if protocolValue == "tcp" {
guard let tcpType = candidate.tcpType else {
continue
}
candidateString.append("tcptype \(tcpType) ")
}
candidateString.append("generation \(candidate.generation)")
appendSdp(candidateString)
}
}
appendSdp("a=rtpmap:111 opus/48000/2")
appendSdp("a=rtpmap:126 telephone-event/8000")
appendSdp("a=fmtp:111 minptime=10; useinbandfec=1; usedtx=1")
appendSdp("a=rtcp:1 IN IP4 0.0.0.0")
appendSdp("a=rtcp-mux")
appendSdp("a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level")
appendSdp("a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time")
appendSdp("a=extmap:5 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01")
appendSdp("a=rtcp-fb:111 transport-cc")
if isAnswer {
appendSdp("a=recvonly")
} else {
if stream.isMain {
appendSdp("a=sendrecv")
} else {
appendSdp("a=sendonly")
appendSdp("a=bundle-only")
}
appendSdp("a=ssrc-group:FID \(stream.audioSsrc)")
appendSdp("a=ssrc:\(stream.audioSsrc) cname:stream\(stream.audioSsrc)")
appendSdp("a=ssrc:\(stream.audioSsrc) msid:stream\(stream.audioSsrc) audio\(stream.audioSsrc)")
appendSdp("a=ssrc:\(stream.audioSsrc) mslabel:audio\(stream.audioSsrc)")
appendSdp("a=ssrc:\(stream.audioSsrc) label:audio\(stream.audioSsrc)")
}
}
}
appendSdp("")
return sdp
}
var bundleStreams: [StreamSpec] = []
bundleStreams.append(StreamSpec(
isMain: true,
audioSsrc: Int(mainStreamAudioSsrc),
isRemoved: false
))
for ssrc in otherSsrcs {
bundleStreams.append(StreamSpec(
isMain: false,
audioSsrc: Int(ssrc),
isRemoved: false
))
}
/*var bundleStreams: [StreamSpec] = []
if let currentState = currentState {
for item in currentState.items {
let isRemoved = !streams.contains(where: { $0.audioSsrc == item.audioSsrc })
bundleStreams.append(StreamSpec(
isMain: item.audioSsrc == mainStreamAudioSsrc,
audioSsrc: item.audioSsrc,
videoSsrc: item.videoSsrc,
isRemoved: isRemoved
))
}
}
for stream in streams {
if bundleStreams.contains(where: { $0.audioSsrc == stream.audioSsrc }) {
continue
}
bundleStreams.append(stream)
}*/
return createSdp(sessionId: sessionId, bundleStreams: bundleStreams)
}
public final class OngoingGroupCallContext {
private final class Impl {
let queue: Queue
let context: GroupCallThreadLocalContext
let sessionId = UInt32.random(in: 0 ..< UInt32(Int32.max))
var mainStreamAudioSsrc: UInt32?
var initialAnswerPayload: String?
var otherSsrcs: [UInt32] = []
let joinPayload = Promise<String>()
init(queue: Queue) {
self.queue = queue
self.context = GroupCallThreadLocalContext(queue: ContextQueueImpl(queue: queue), relaySdpAnswer: { _ in
}, incomingVideoStreamListUpdated: { _ in
}, videoCapturer: nil)
let queue = self.queue
self.context.emitOffer(adjustSdp: { sdp in
return sdp
}, completion: { [weak self] offerSdp in
queue.async {
guard let strongSelf = self else {
return
}
if let payload = parseSdpIntoJoinPayload(sdp: offerSdp) {
strongSelf.mainStreamAudioSsrc = payload.audioSsrc
strongSelf.joinPayload.set(.single(payload.payload))
}
}
})
}
func setJoinResponse(payload: String, ssrcs: [Int32]) {
guard let mainStreamAudioSsrc = self.mainStreamAudioSsrc else {
return
}
if let sdp = parseJoinResponseIntoSdp(sessionId: self.sessionId, mainStreamAudioSsrc: mainStreamAudioSsrc, payload: payload, isAnswer: true, otherSsrcs: []) {
self.initialAnswerPayload = payload
self.context.setOfferSdp(sdp, isPartial: true)
self.addSsrcs(ssrcs: ssrcs)
}
}
func addSsrcs(ssrcs: [Int32]) {
if ssrcs.isEmpty {
return
}
guard let mainStreamAudioSsrc = self.mainStreamAudioSsrc else {
return
}
guard let initialAnswerPayload = self.initialAnswerPayload else {
return
}
let mappedSsrcs = ssrcs.map(UInt32.init(bitPattern:))
var otherSsrcs = self.otherSsrcs
for ssrc in mappedSsrcs {
if ssrc == mainStreamAudioSsrc {
continue
}
if !otherSsrcs.contains(ssrc) {
otherSsrcs.append(ssrc)
}
}
if self.otherSsrcs != otherSsrcs {
self.otherSsrcs = otherSsrcs
if let sdp = parseJoinResponseIntoSdp(sessionId: self.sessionId, mainStreamAudioSsrc: mainStreamAudioSsrc, payload: initialAnswerPayload, isAnswer: false, otherSsrcs: self.otherSsrcs) {
self.context.setOfferSdp(sdp, isPartial: false)
}
}
}
func setIsMuted(_ isMuted: Bool) {
}
}
private let queue = Queue()
private let impl: QueueLocalObject<Impl>
public var joinPayload: Signal<String, NoError> {
return Signal { subscriber in
let disposable = MetaDisposable()
self.impl.with { impl in
disposable.set(impl.joinPayload.get().start(next: { value in
subscriber.putNext(value)
}))
}
return disposable
}
}
public init() {
let queue = self.queue
self.impl = QueueLocalObject(queue: queue, generate: {
return Impl(queue: queue)
})
}
public func setIsMuted(_ isMuted: Bool) {
self.impl.with { impl in
impl.setIsMuted(isMuted)
}
}
public func setJoinResponse(payload: String, ssrcs: [Int32]) {
self.impl.with { impl in
impl.setJoinResponse(payload: payload, ssrcs: ssrcs)
}
}
public func addSsrcs(ssrcs: [Int32]) {
self.impl.with { impl in
impl.addSsrcs(ssrcs: ssrcs)
}
}
}