mirror of
https://github.com/BushlanovDev/max-bot-api-client-php.git
synced 2026-08-17 12:13:12 +00:00
Added getAdmins & deleteAdmins & getMembers & removeMember
This commit is contained in:
@@ -41,12 +41,12 @@
|
||||
- [x] `DELETE /chats/{chatId}/pin` (`unpinMessage`) — *Открепление сообщения.*
|
||||
- [x] `GET /chats/{chatId}/members/me` (`getMembership`) — *Получение информации о членстве бота в чате.*
|
||||
- [x] `DELETE /chats/{chatId}/members/me` (`leaveChat`) — *Выход бота из чата.*
|
||||
- [ ] `GET /chats/{chatId}/members/admins` (`getAdmins`) — *Получение администраторов чата.*
|
||||
- [x] `GET /chats/{chatId}/members/admins` (`getAdmins`) — *Получение администраторов чата.*
|
||||
- [ ] `POST /chats/{chatId}/members/admins` (`postAdmins`) — *Назначение администраторов чата.*
|
||||
- [ ] `DELETE /chats/{chatId}/members/admins/{userId}` (`deleteAdmins`) — *Снятие прав администратора.*
|
||||
- [ ] `GET /chats/{chatId}/members` (`getMembers`) — *Получение участников чата.*
|
||||
- [x] `DELETE /chats/{chatId}/members/admins/{userId}` (`deleteAdmins`) — *Снятие прав администратора.*
|
||||
- [x] `GET /chats/{chatId}/members` (`getMembers`) — *Получение участников чата.*
|
||||
- [ ] `POST /chats/{chatId}/members` (`addMembers`) — *Добавление участников в чат.*
|
||||
- [ ] `DELETE /chats/{chatId}/members` (`removeMember`) — *Удаление участника из чата.*
|
||||
- [x] `DELETE /chats/{chatId}/members` (`removeMember`) — *Удаление участника из чата.*
|
||||
|
||||
#### Subscriptions
|
||||
|
||||
@@ -71,4 +71,4 @@
|
||||
|
||||
## Лицензия
|
||||
|
||||
Данная библиотека распространяется по лицензии MIT - подробности см. в файле [LICENSE](LICENSE).
|
||||
Данная библиотека распространяется под лицензией MIT - подробности см. в файле [LICENSE](LICENSE).
|
||||
|
||||
+112
-1
@@ -22,6 +22,7 @@ use BushlanovDev\MaxMessengerBot\Models\BotInfo;
|
||||
use BushlanovDev\MaxMessengerBot\Models\Chat;
|
||||
use BushlanovDev\MaxMessengerBot\Models\ChatList;
|
||||
use BushlanovDev\MaxMessengerBot\Models\ChatMember;
|
||||
use BushlanovDev\MaxMessengerBot\Models\ChatMembersList;
|
||||
use BushlanovDev\MaxMessengerBot\Models\Message;
|
||||
use BushlanovDev\MaxMessengerBot\Models\MessageLink;
|
||||
use BushlanovDev\MaxMessengerBot\Models\Result;
|
||||
@@ -60,6 +61,9 @@ class Api
|
||||
private const string ACTION_CHATS_ACTIONS = '/chats/%d/actions';
|
||||
private const string ACTION_CHATS_PIN = '/chats/%d/pin';
|
||||
private const string ACTION_CHATS_MEMBERS_ME = '/chats/%d/members/me';
|
||||
private const string ACTION_CHATS_MEMBERS_ADMINS = '/chats/%d/members/admins';
|
||||
private const string ACTION_CHATS_MEMBERS_ADMINS_ID = '/chats/%d/members/admins/%d';
|
||||
private const string ACTION_CHATS_MEMBERS = '/chats/%d/members';
|
||||
private const string ACTION_UPDATES = '/updates';
|
||||
|
||||
private readonly ClientApiInterface $client;
|
||||
@@ -703,7 +707,7 @@ class Api
|
||||
$response = $this->client->request(
|
||||
self::METHOD_GET,
|
||||
self::ACTION_MESSAGES,
|
||||
array_filter($query, fn ($value) => $value !== null)
|
||||
array_filter($query, fn($value) => $value !== null),
|
||||
);
|
||||
|
||||
return $this->modelFactory->createMessages($response);
|
||||
@@ -779,4 +783,111 @@ class Api
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all chat administrators. The bot must be an administrator in the requested chat.
|
||||
*
|
||||
* @param int $chatId Chat identifier.
|
||||
*
|
||||
* @return ChatMembersList
|
||||
* @throws ClientApiException
|
||||
* @throws NetworkException
|
||||
* @throws ReflectionException
|
||||
* @throws SerializationException
|
||||
*/
|
||||
public function getAdmins(int $chatId): ChatMembersList
|
||||
{
|
||||
return $this->modelFactory->createChatMembersList(
|
||||
$this->client->request(
|
||||
self::METHOD_GET,
|
||||
sprintf(self::ACTION_CHATS_MEMBERS_ADMINS, $chatId),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a paginated list of users who are participating in a chat.
|
||||
*
|
||||
* @param int $chatId The identifier of the chat.
|
||||
* @param int[]|null $userIds A list of user identifiers to get their specific membership.
|
||||
* When this parameter is passed, `count` and `marker` are ignored.
|
||||
* @param int|null $marker The pagination marker to get the next page of members.
|
||||
* @param int|null $count The number of members to return (1-100, default is 20).
|
||||
*
|
||||
* @return ChatMembersList
|
||||
* @throws ClientApiException
|
||||
* @throws NetworkException
|
||||
* @throws ReflectionException
|
||||
* @throws SerializationException
|
||||
*/
|
||||
public function getMembers(
|
||||
int $chatId,
|
||||
?array $userIds = null,
|
||||
?int $marker = null,
|
||||
?int $count = null
|
||||
): ChatMembersList {
|
||||
$query = [
|
||||
'user_ids' => $userIds !== null ? implode(',', $userIds) : null,
|
||||
'marker' => $marker,
|
||||
'count' => $count,
|
||||
];
|
||||
|
||||
return $this->modelFactory->createChatMembersList(
|
||||
$this->client->request(
|
||||
self::METHOD_GET,
|
||||
sprintf(self::ACTION_CHATS_MEMBERS, $chatId),
|
||||
array_filter($query, fn($value) => $value !== null),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Revokes admin rights from a user in the chat.
|
||||
*
|
||||
* @param int $chatId The identifier of the chat.
|
||||
* @param int $userId The identifier of the user to revoke admin rights from.
|
||||
*
|
||||
* @return Result
|
||||
* @throws ClientApiException
|
||||
* @throws NetworkException
|
||||
* @throws ReflectionException
|
||||
* @throws SerializationException
|
||||
*/
|
||||
public function deleteAdmins(int $chatId, int $userId): Result
|
||||
{
|
||||
return $this->modelFactory->createResult(
|
||||
$this->client->request(
|
||||
self::METHOD_DELETE,
|
||||
sprintf(self::ACTION_CHATS_MEMBERS_ADMINS_ID, $chatId, $userId),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a member from a chat. The bot may require additional permissions.
|
||||
*
|
||||
* @param int $chatId The identifier of the chat.
|
||||
* @param int $userId The identifier of the user to remove.
|
||||
* @param bool $block Set to true if the user should also be blocked in the chat.
|
||||
* Applicable only for chats with a public or private link.
|
||||
*
|
||||
* @return Result
|
||||
* @throws ClientApiException
|
||||
* @throws NetworkException
|
||||
* @throws ReflectionException
|
||||
* @throws SerializationException
|
||||
*/
|
||||
public function removeMember(int $chatId, int $userId, bool $block = false): Result
|
||||
{
|
||||
return $this->modelFactory->createResult(
|
||||
$this->client->request(
|
||||
self::METHOD_DELETE,
|
||||
sprintf(self::ACTION_CHATS_MEMBERS, $chatId),
|
||||
[
|
||||
'user_id' => $userId,
|
||||
'block' => $block,
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ use BushlanovDev\MaxMessengerBot\Models\BotInfo;
|
||||
use BushlanovDev\MaxMessengerBot\Models\Chat;
|
||||
use BushlanovDev\MaxMessengerBot\Models\ChatList;
|
||||
use BushlanovDev\MaxMessengerBot\Models\ChatMember;
|
||||
use BushlanovDev\MaxMessengerBot\Models\ChatMembersList;
|
||||
use BushlanovDev\MaxMessengerBot\Models\Message;
|
||||
use BushlanovDev\MaxMessengerBot\Models\Result;
|
||||
use BushlanovDev\MaxMessengerBot\Models\Subscription;
|
||||
@@ -220,4 +221,17 @@ class ModelFactory
|
||||
{
|
||||
return ChatMember::fromArray($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a ChatMembersList object from raw API data.
|
||||
*
|
||||
* @param array<string, mixed> $data
|
||||
*
|
||||
* @return ChatMembersList
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
public function createChatMembersList(array $data): ChatMembersList
|
||||
{
|
||||
return ChatMembersList::fromArray($data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace BushlanovDev\MaxMessengerBot\Models;
|
||||
|
||||
use BushlanovDev\MaxMessengerBot\Attributes\ArrayOf;
|
||||
|
||||
/**
|
||||
* Represents a paginated list of chat members.
|
||||
*/
|
||||
final readonly class ChatMembersList extends AbstractModel
|
||||
{
|
||||
/**
|
||||
* @param ChatMember[] $members A list of chat members.
|
||||
* @param int|null $marker A pointer to the next page of data. Null if this is the last page.
|
||||
*/
|
||||
public function __construct(
|
||||
#[ArrayOf(ChatMember::class)]
|
||||
public array $members,
|
||||
public ?int $marker,
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,7 @@ use BushlanovDev\MaxMessengerBot\Models\BotInfo;
|
||||
use BushlanovDev\MaxMessengerBot\Models\Chat;
|
||||
use BushlanovDev\MaxMessengerBot\Models\ChatList;
|
||||
use BushlanovDev\MaxMessengerBot\Models\ChatMember;
|
||||
use BushlanovDev\MaxMessengerBot\Models\ChatMembersList;
|
||||
use BushlanovDev\MaxMessengerBot\Models\Message;
|
||||
use BushlanovDev\MaxMessengerBot\Models\MessageBody;
|
||||
use BushlanovDev\MaxMessengerBot\Models\Recipient;
|
||||
@@ -105,6 +106,7 @@ use RuntimeException;
|
||||
#[UsesClass(ChatList::class)]
|
||||
#[UsesClass(ChatMember::class)]
|
||||
#[UsesClass(ArrayOf::class)]
|
||||
#[UsesClass(ChatMembersList::class)]
|
||||
final class ApiTest extends TestCase
|
||||
{
|
||||
use PHPMock;
|
||||
@@ -1679,4 +1681,199 @@ final class ApiTest extends TestCase
|
||||
|
||||
$this->api->pinMessage($chatId, $messageId);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function getAdminsReturnsChatMembersList(): void
|
||||
{
|
||||
$chatId = 98765;
|
||||
$uri = sprintf('/chats/%d/members/admins', $chatId);
|
||||
|
||||
$rawResponse = [
|
||||
'members' => [
|
||||
[
|
||||
'user_id' => 1,
|
||||
'first_name' => 'AdminBot',
|
||||
'is_bot' => true,
|
||||
'last_activity_time' => 1,
|
||||
'last_access_time' => 2,
|
||||
'is_owner' => false,
|
||||
'is_admin' => true,
|
||||
'join_time' => 0
|
||||
]
|
||||
],
|
||||
'marker' => null
|
||||
];
|
||||
$expectedList = ChatMembersList::fromArray($rawResponse);
|
||||
|
||||
$this->clientMock
|
||||
->expects($this->once())
|
||||
->method('request')
|
||||
->with('GET', $uri)
|
||||
->willReturn($rawResponse);
|
||||
|
||||
$this->modelFactoryMock
|
||||
->expects($this->once())
|
||||
->method('createChatMembersList')
|
||||
->with($rawResponse)
|
||||
->willReturn($expectedList);
|
||||
|
||||
$result = $this->api->getAdmins($chatId);
|
||||
|
||||
$this->assertSame($expectedList, $result);
|
||||
$this->assertCount(1, $result->members);
|
||||
$this->assertTrue($result->members[0]->isAdmin);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function getMembersWithPaginationCallsClientCorrectly(): void
|
||||
{
|
||||
$chatId = 12345;
|
||||
$count = 50;
|
||||
$marker = 98765;
|
||||
$uri = sprintf('/chats/%d/members', $chatId);
|
||||
$expectedQuery = ['count' => $count, 'marker' => $marker];
|
||||
|
||||
$rawResponse = ['members' => [], 'marker' => 123];
|
||||
$expectedList = new ChatMembersList([], 123);
|
||||
|
||||
$this->clientMock
|
||||
->expects($this->once())
|
||||
->method('request')
|
||||
->with('GET', $uri, $expectedQuery)
|
||||
->willReturn($rawResponse);
|
||||
|
||||
$this->modelFactoryMock
|
||||
->expects($this->once())
|
||||
->method('createChatMembersList')
|
||||
->with($rawResponse)
|
||||
->willReturn($expectedList);
|
||||
|
||||
$result = $this->api->getMembers($chatId, null, $marker, $count);
|
||||
|
||||
$this->assertSame($expectedList, $result);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function getMembersWithUserIdsCallsClientCorrectly(): void
|
||||
{
|
||||
$chatId = 54321;
|
||||
$userIds = [101, 202, 303];
|
||||
$uri = sprintf('/chats/%d/members', $chatId);
|
||||
$expectedQuery = ['user_ids' => '101,202,303'];
|
||||
|
||||
$rawResponse = [
|
||||
'members' => [
|
||||
[
|
||||
'user_id' => 101,
|
||||
'first_name' => 'User1',
|
||||
'is_bot' => false,
|
||||
'last_activity_time' => 1,
|
||||
'last_access_time' => 2,
|
||||
'is_owner' => false,
|
||||
'is_admin' => false,
|
||||
'join_time' => 0,
|
||||
]
|
||||
],
|
||||
'marker' => null,
|
||||
];
|
||||
$expectedList = ChatMembersList::fromArray($rawResponse);
|
||||
|
||||
$this->clientMock
|
||||
->expects($this->once())
|
||||
->method('request')
|
||||
->with('GET', $uri, $expectedQuery)
|
||||
->willReturn($rawResponse);
|
||||
|
||||
$this->modelFactoryMock
|
||||
->expects($this->once())
|
||||
->method('createChatMembersList')
|
||||
->with($rawResponse)
|
||||
->willReturn($expectedList);
|
||||
|
||||
$result = $this->api->getMembers($chatId, $userIds);
|
||||
|
||||
$this->assertSame($expectedList, $result);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function deleteAdminsCallsClientCorrectly(): void
|
||||
{
|
||||
$chatId = 12345;
|
||||
$userId = 987;
|
||||
$uri = sprintf('/chats/%d/members/admins/%d', $chatId, $userId);
|
||||
$rawResponse = ['success' => true];
|
||||
$expectedResult = new Result(true, null);
|
||||
|
||||
$this->clientMock
|
||||
->expects($this->once())
|
||||
->method('request')
|
||||
->with(self::equalTo('DELETE'), self::equalTo($uri))
|
||||
->willReturn($rawResponse);
|
||||
|
||||
$this->modelFactoryMock
|
||||
->expects($this->once())
|
||||
->method('createResult')
|
||||
->with($rawResponse)
|
||||
->willReturn($expectedResult);
|
||||
|
||||
$result = $this->api->deleteAdmins($chatId, $userId);
|
||||
|
||||
$this->assertSame($expectedResult, $result);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function removeMemberCallsClientWithDefaultBlockValue(): void
|
||||
{
|
||||
$chatId = 12345;
|
||||
$userId = 678;
|
||||
$uri = sprintf('/chats/%d/members', $chatId);
|
||||
$expectedQuery = ['user_id' => $userId];
|
||||
|
||||
$rawResponse = ['success' => true];
|
||||
$expectedResult = new Result(true, null);
|
||||
|
||||
$this->clientMock
|
||||
->expects($this->once())
|
||||
->method('request')
|
||||
->with('DELETE', $uri, $expectedQuery)
|
||||
->willReturn($rawResponse);
|
||||
|
||||
$this->modelFactoryMock
|
||||
->expects($this->once())
|
||||
->method('createResult')
|
||||
->with($rawResponse)
|
||||
->willReturn($expectedResult);
|
||||
|
||||
$result = $this->api->removeMember($chatId, $userId);
|
||||
|
||||
$this->assertSame($expectedResult, $result);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function removeMemberCallsClientWithBlockTrue(): void
|
||||
{
|
||||
$chatId = 54321;
|
||||
$userId = 910;
|
||||
$uri = sprintf('/chats/%d/members', $chatId);
|
||||
$expectedQuery = ['user_id' => $userId, 'block' => true];
|
||||
|
||||
$rawResponse = ['success' => true];
|
||||
$expectedResult = new Result(true, null);
|
||||
|
||||
$this->clientMock
|
||||
->expects($this->once())
|
||||
->method('request')
|
||||
->with('DELETE', $uri, $expectedQuery)
|
||||
->willReturn($rawResponse);
|
||||
|
||||
$this->modelFactoryMock
|
||||
->expects($this->once())
|
||||
->method('createResult')
|
||||
->with($rawResponse)
|
||||
->willReturn($expectedResult);
|
||||
|
||||
$result = $this->api->removeMember($chatId, $userId, true);
|
||||
|
||||
$this->assertSame($expectedResult, $result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ use BushlanovDev\MaxMessengerBot\Models\BotInfo;
|
||||
use BushlanovDev\MaxMessengerBot\Models\Chat;
|
||||
use BushlanovDev\MaxMessengerBot\Models\ChatList;
|
||||
use BushlanovDev\MaxMessengerBot\Models\ChatMember;
|
||||
use BushlanovDev\MaxMessengerBot\Models\ChatMembersList;
|
||||
use BushlanovDev\MaxMessengerBot\Models\Image;
|
||||
use BushlanovDev\MaxMessengerBot\Models\Message;
|
||||
use BushlanovDev\MaxMessengerBot\Models\MessageBody;
|
||||
@@ -53,6 +54,7 @@ use PHPUnit\Framework\TestCase;
|
||||
#[UsesClass(MessageChatCreatedUpdate::class)]
|
||||
#[UsesClass(ChatList::class)]
|
||||
#[UsesClass(ChatMember::class)]
|
||||
#[UsesClass(ChatMembersList::class)]
|
||||
final class ModelFactoryTest extends TestCase
|
||||
{
|
||||
private ModelFactory $factory;
|
||||
@@ -419,4 +421,63 @@ final class ModelFactoryTest extends TestCase
|
||||
$this->assertEmpty($this->factory->createMessages(['messages' => []]));
|
||||
$this->assertEmpty($this->factory->createMessages([]));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function createChatMembersListSuccessfully(): void
|
||||
{
|
||||
$rawData = [
|
||||
'members' => [
|
||||
[
|
||||
'user_id' => 101,
|
||||
'first_name' => 'Admin1',
|
||||
'is_bot' => false,
|
||||
'last_activity_time' => 1,
|
||||
'last_access_time' => 2,
|
||||
'is_owner' => true,
|
||||
'is_admin' => true,
|
||||
'join_time' => 0,
|
||||
],
|
||||
[
|
||||
'user_id' => 102,
|
||||
'first_name' => 'Admin2',
|
||||
'is_bot' => true,
|
||||
'last_activity_time' => 3,
|
||||
'last_access_time' => 4,
|
||||
'is_owner' => false,
|
||||
'is_admin' => true,
|
||||
'join_time' => 5,
|
||||
],
|
||||
],
|
||||
'marker' => 98765,
|
||||
];
|
||||
|
||||
$list = $this->factory->createChatMembersList($rawData);
|
||||
|
||||
$this->assertInstanceOf(ChatMembersList::class, $list);
|
||||
$this->assertCount(2, $list->members);
|
||||
$this->assertSame(98765, $list->marker);
|
||||
|
||||
$this->assertInstanceOf(ChatMember::class, $list->members[0]);
|
||||
$this->assertSame(101, $list->members[0]->userId);
|
||||
$this->assertTrue($list->members[0]->isOwner);
|
||||
|
||||
$this->assertInstanceOf(ChatMember::class, $list->members[1]);
|
||||
$this->assertSame(102, $list->members[1]->userId);
|
||||
$this->assertTrue($list->members[1]->isBot);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function createChatMembersListHandlesEmptyResponse(): void
|
||||
{
|
||||
$rawData = [
|
||||
'members' => [],
|
||||
'marker' => null,
|
||||
];
|
||||
|
||||
$list = $this->factory->createChatMembersList($rawData);
|
||||
|
||||
$this->assertInstanceOf(ChatMembersList::class, $list);
|
||||
$this->assertEmpty($list->members);
|
||||
$this->assertNull($list->marker);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace BushlanovDev\MaxMessengerBot\Tests\Models;
|
||||
|
||||
use BushlanovDev\MaxMessengerBot\Attributes\ArrayOf;
|
||||
use BushlanovDev\MaxMessengerBot\Models\ChatMember;
|
||||
use BushlanovDev\MaxMessengerBot\Models\ChatMembersList;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\Attributes\UsesClass;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
#[CoversClass(ChatMembersList::class)]
|
||||
#[UsesClass(ChatMember::class)]
|
||||
#[UsesClass(ArrayOf::class)]
|
||||
final class ChatMembersListTest extends TestCase
|
||||
{
|
||||
#[Test]
|
||||
public function canBeCreatedWithData(): void
|
||||
{
|
||||
$data = [
|
||||
'members' => [
|
||||
[
|
||||
'user_id' => 101,
|
||||
'first_name' => 'Admin1',
|
||||
'is_bot' => false,
|
||||
'last_activity_time' => 1,
|
||||
'last_access_time' => 2,
|
||||
'is_owner' => true,
|
||||
'is_admin' => true,
|
||||
'join_time' => 0,
|
||||
],
|
||||
],
|
||||
'marker' => 12345,
|
||||
];
|
||||
|
||||
$list = ChatMembersList::fromArray($data);
|
||||
|
||||
$this->assertInstanceOf(ChatMembersList::class, $list);
|
||||
$this->assertCount(1, $list->members);
|
||||
$this->assertInstanceOf(ChatMember::class, $list->members[0]);
|
||||
$this->assertSame(101, $list->members[0]->userId);
|
||||
$this->assertSame(12345, $list->marker);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function canBeCreatedWithEmptyMembersAndNullMarker(): void
|
||||
{
|
||||
$data = ['members' => [], 'marker' => null];
|
||||
$list = ChatMembersList::fromArray($data);
|
||||
|
||||
$this->assertIsArray($list->members);
|
||||
$this->assertEmpty($list->members);
|
||||
$this->assertNull($list->marker);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user