Skip to content

Commit 7bf4f69

Browse files
authored
Merge pull request #1915 from danog/fix-group-call-ended-event
Group calls: report a call that ended (GROUPCALL_INVALID) as discarded
2 parents 25a60b0 + 9df2ab2 commit 7bf4f69

2 files changed

Lines changed: 68 additions & 6 deletions

File tree

‎src/GroupCall/GroupCallController.php‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -775,11 +775,34 @@ private function checkJoined(): void
775775
/**
776776
* The call ended, or we may not be in it anymore (GROUPCALL_FORBIDDEN or GROUPCALL_INVALID): leave it, as
777777
* TDLib does.
778+
*
779+
* A call that ended (GROUPCALL_INVALID) is reported to the event handler as discarded, as if we got its
780+
* groupCallDiscarded: we may never get one (a live story ends with an updateStory deleting it).
778781
*/
779782
private function onEnded(string $rpc): void
780783
{
781784
$this->log("$this ended, or we may not be in it anymore ($rpc): leaving it.", Logger::NOTICE);
785+
$discarded = $this->public->discarded;
782786
$this->cleanup();
787+
if ($rpc !== 'GROUPCALL_INVALID' || $discarded) {
788+
return;
789+
}
790+
$update = [
791+
'_' => 'updateGroupCall',
792+
'call' => ['_' => 'groupCallDiscarded', 'id' => $this->public->id, 'access_hash' => $this->public->accessHash, 'duration' => 0],
793+
];
794+
if ($this->public instanceof LiveStory) {
795+
$update['live_story'] = true;
796+
}
797+
$peerId = $this->public->peerId;
798+
if ($peerId !== null) {
799+
$update['peer'] = match (true) {
800+
DialogId::isUser($peerId) => ['_' => 'peerUser', 'user_id' => $peerId],
801+
DialogId::isChat($peerId) => ['_' => 'peerChat', 'chat_id' => DialogId::toChatId($peerId)],
802+
default => ['_' => 'peerChannel', 'channel_id' => DialogId::toSupergroupOrChannelId($peerId)],
803+
};
804+
}
805+
$this->API->saveUpdate($update);
783806
}
784807

785808
/**

‎tests/danog/MadelineProto/GroupCallMembershipTest.php‎

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
use DG\BypassFinals;
3434
use Exception;
3535
use PHPUnit\Framework\TestCase;
36-
use ReflectionClass;
3736
use ReflectionProperty;
3837

3938
use function Amp\delay;
@@ -64,12 +63,18 @@ protected function setUp(): void
6463
BypassFinals::enable();
6564
}
6665

66+
/** @var list<array> The updates saved. */
67+
private array $updates = [];
68+
6769
private function client(): MTProto
6870
{
69-
$API = (new ReflectionClass(MTProto::class))->newInstanceWithoutConstructor();
71+
$API = $this->getMockBuilder(MTProto::class)->disableOriginalConstructor()->onlyMethods(['saveUpdate'])->getMock();
72+
$API->method('saveUpdate')->willReturnCallback(function (array $update): void {
73+
$this->updates[] = $update;
74+
});
7075
$API->logger = new Logger((new LoggerSettings)->setType(Logger::ECHO_LOGGER)->setLevel(Logger::FATAL_ERROR));
7176
$API->wrapper = new APIWrapper(new SessionPaths(sys_get_temp_dir().'/madeline-group-call-membership'));
72-
(new ReflectionProperty($API, 'settings'))->setValue($API, new Settings);
77+
(new ReflectionProperty(MTProto::class, 'settings'))->setValue($API, new Settings);
7378
$connection = $this->createMock(Connection::class);
7479
$connection->method('methodCallAsyncRead')->willReturnCallback(function (string $method, array $args): mixed {
7580
$this->calls[] = $method;
@@ -82,7 +87,7 @@ private function client(): MTProto
8287
return $API;
8388
}
8489

85-
private function joinedCall(MTProto $API): GroupCallController
90+
private function joinedCall(MTProto $API, ?int $peerId = null, bool $liveStory = false): GroupCallController
8691
{
8792
$controller = new GroupCallController($API, [
8893
'_' => 'groupCall',
@@ -103,10 +108,10 @@ private function joinedCall(MTProto $API): GroupCallController
103108
'record_video_active' => false,
104109
'conference' => false,
105110
'version' => 1,
106-
]);
111+
], $peerId, $liveStory);
107112
(new ReflectionProperty($controller, 'callState'))->setValue($controller, GroupCallState::JOINED);
108113
(new ReflectionProperty($controller, 'source'))->setValue($controller, self::SOURCE);
109-
(new ReflectionProperty($API, 'groupCalls'))->setValue($API, [1 => $controller]);
114+
(new ReflectionProperty(MTProto::class, 'groupCalls'))->setValue($API, [1 => $controller]);
110115
return $controller;
111116
}
112117

@@ -143,6 +148,40 @@ public function testAnEndedCallIsLeft(string $rpc): void
143148
$this->assertSame([], $API->getAllGroupCalls());
144149
}
145150

151+
/**
152+
* A call that ended is reported to the event handler as discarded: a live story ends with an updateStory, without
153+
* the groupCallDiscarded the event handler waits for (magnaluna didn't start its live story again).
154+
*/
155+
public function testAnEndedCallIsReportedAsDiscarded(): void
156+
{
157+
$API = $this->client();
158+
$controller = $this->joinedCall($API, 783732906, liveStory: true);
159+
$this->answer = static fn () => throw RPCErrorException::make('GROUPCALL_INVALID', 400, '');
160+
161+
self::check($controller);
162+
163+
$this->assertSame([[
164+
'_' => 'updateGroupCall',
165+
'call' => ['_' => 'groupCallDiscarded', 'id' => 1, 'access_hash' => 1234, 'duration' => 0],
166+
'live_story' => true,
167+
'peer' => ['_' => 'peerUser', 'user_id' => 783732906],
168+
]], $this->updates);
169+
}
170+
171+
/**
172+
* A call we may just not be in anymore didn't end: it isn't reported as discarded.
173+
*/
174+
public function testAForbiddenCallIsNotReportedAsDiscarded(): void
175+
{
176+
$controller = $this->joinedCall($this->client(), -1001412768105);
177+
$this->answer = static fn () => throw RPCErrorException::make('GROUPCALL_FORBIDDEN', 403, '');
178+
179+
self::check($controller);
180+
181+
$this->assertSame(GroupCallState::LEFT, $controller->getCallState());
182+
$this->assertSame([], $this->updates);
183+
}
184+
146185
/**
147186
* A failed WebRTC connection is rejoined right away, even if the server still lists our source: it never
148187
* recovers by itself (after a network outage, every call of a bot stayed failed for hours, sending nothing).

0 commit comments

Comments
 (0)