diff options
Diffstat (limited to 'tests/lib/Comments/ManagerTest.php')
-rw-r--r-- | tests/lib/Comments/ManagerTest.php | 2560 |
1 files changed, 2560 insertions, 0 deletions
diff --git a/tests/lib/Comments/ManagerTest.php b/tests/lib/Comments/ManagerTest.php new file mode 100644 index 00000000000..bd991a5692a --- /dev/null +++ b/tests/lib/Comments/ManagerTest.php @@ -0,0 +1,2560 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only + */ +namespace Test\Comments; + +use OC\Comments\Comment; +use OC\Comments\Manager; +use OC\EmojiHelper; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\Comments\IComment; +use OCP\Comments\ICommentsEventHandler; +use OCP\Comments\ICommentsManager; +use OCP\Comments\NotFoundException; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\Files\Folder; +use OCP\Files\IRootFolder; +use OCP\IConfig; +use OCP\IDBConnection; +use OCP\IInitialStateService; +use OCP\IUser; +use OCP\IUserManager; +use OCP\Server; +use Psr\Log\LoggerInterface; +use Test\TestCase; + +/** + * Class ManagerTest + * + * @group DB + */ +class ManagerTest extends TestCase { + /** @var IDBConnection */ + private $connection; + /** @var \PHPUnit\Framework\MockObject\MockObject|IRootFolder */ + private $rootFolder; + + protected function setUp(): void { + parent::setUp(); + + $this->connection = Server::get(IDBConnection::class); + $this->rootFolder = $this->createMock(IRootFolder::class); + + $sql = $this->connection->getDatabasePlatform()->getTruncateTableSQL('`*PREFIX*comments`'); + $this->connection->prepare($sql)->execute(); + $sql = $this->connection->getDatabasePlatform()->getTruncateTableSQL('`*PREFIX*reactions`'); + $this->connection->prepare($sql)->execute(); + } + + protected function addDatabaseEntry($parentId, $topmostParentId, $creationDT = null, $latestChildDT = null, $objectId = null, $expireDate = null) { + $creationDT ??= new \DateTime(); + $latestChildDT ??= new \DateTime('yesterday'); + $objectId ??= 'file64'; + + $qb = $this->connection->getQueryBuilder(); + $qb + ->insert('comments') + ->values([ + 'parent_id' => $qb->createNamedParameter($parentId), + 'topmost_parent_id' => $qb->createNamedParameter($topmostParentId), + 'children_count' => $qb->createNamedParameter(2), + 'actor_type' => $qb->createNamedParameter('users'), + 'actor_id' => $qb->createNamedParameter('alice'), + 'message' => $qb->createNamedParameter('nice one'), + 'verb' => $qb->createNamedParameter('comment'), + 'creation_timestamp' => $qb->createNamedParameter($creationDT, IQueryBuilder::PARAM_DATETIME_MUTABLE), + 'latest_child_timestamp' => $qb->createNamedParameter($latestChildDT, IQueryBuilder::PARAM_DATETIME_MUTABLE), + 'object_type' => $qb->createNamedParameter('files'), + 'object_id' => $qb->createNamedParameter($objectId), + 'expire_date' => $qb->createNamedParameter($expireDate, IQueryBuilder::PARAM_DATETIME_MUTABLE), + 'reference_id' => $qb->createNamedParameter('referenceId'), + 'meta_data' => $qb->createNamedParameter(json_encode(['last_edit_actor_id' => 'admin'])), + ]) + ->executeStatement(); + + return $qb->getLastInsertId(); + } + + protected function getManager() { + return new Manager( + $this->connection, + $this->createMock(LoggerInterface::class), + $this->createMock(IConfig::class), + $this->createMock(ITimeFactory::class), + new EmojiHelper($this->connection), + $this->createMock(IInitialStateService::class), + $this->rootFolder, + $this->createMock(IEventDispatcher::class), + ); + } + + + public function testGetCommentNotFound(): void { + $this->expectException(NotFoundException::class); + + $manager = $this->getManager(); + $manager->get('22'); + } + + + public function testGetCommentNotFoundInvalidInput(): void { + $this->expectException(\InvalidArgumentException::class); + + $manager = $this->getManager(); + $manager->get('unexisting22'); + } + + public function testGetComment(): void { + $manager = $this->getManager(); + + $creationDT = new \DateTime('yesterday'); + $latestChildDT = new \DateTime(); + + $qb = Server::get(IDBConnection::class)->getQueryBuilder(); + $qb + ->insert('comments') + ->values([ + 'parent_id' => $qb->createNamedParameter('2'), + 'topmost_parent_id' => $qb->createNamedParameter('1'), + 'children_count' => $qb->createNamedParameter(2), + 'actor_type' => $qb->createNamedParameter('users'), + 'actor_id' => $qb->createNamedParameter('alice'), + 'message' => $qb->createNamedParameter('nice one'), + 'verb' => $qb->createNamedParameter('comment'), + 'creation_timestamp' => $qb->createNamedParameter($creationDT, 'datetime'), + 'latest_child_timestamp' => $qb->createNamedParameter($latestChildDT, 'datetime'), + 'object_type' => $qb->createNamedParameter('files'), + 'object_id' => $qb->createNamedParameter('file64'), + 'reference_id' => $qb->createNamedParameter('referenceId'), + 'meta_data' => $qb->createNamedParameter(json_encode(['last_edit_actor_id' => 'admin'])), + ]) + ->executeStatement(); + + $id = (string)$qb->getLastInsertId(); + + $comment = $manager->get($id); + $this->assertInstanceOf(IComment::class, $comment); + $this->assertSame($id, $comment->getId()); + $this->assertSame('2', $comment->getParentId()); + $this->assertSame('1', $comment->getTopmostParentId()); + $this->assertSame(2, $comment->getChildrenCount()); + $this->assertSame('users', $comment->getActorType()); + $this->assertSame('alice', $comment->getActorId()); + $this->assertSame('nice one', $comment->getMessage()); + $this->assertSame('comment', $comment->getVerb()); + $this->assertSame('files', $comment->getObjectType()); + $this->assertSame('file64', $comment->getObjectId()); + $this->assertEquals($creationDT->getTimestamp(), $comment->getCreationDateTime()->getTimestamp()); + $this->assertEquals($latestChildDT->getTimestamp(), $comment->getLatestChildDateTime()->getTimestamp()); + $this->assertEquals('referenceId', $comment->getReferenceId()); + $this->assertEquals(['last_edit_actor_id' => 'admin'], $comment->getMetaData()); + } + + + public function testGetTreeNotFound(): void { + $this->expectException(NotFoundException::class); + + $manager = $this->getManager(); + $manager->getTree('22'); + } + + + public function testGetTreeNotFoundInvalidIpnut(): void { + $this->expectException(\InvalidArgumentException::class); + + $manager = $this->getManager(); + $manager->getTree('unexisting22'); + } + + public function testGetTree(): void { + $headId = $this->addDatabaseEntry(0, 0); + + $this->addDatabaseEntry($headId, $headId, new \DateTime('-3 hours')); + $this->addDatabaseEntry($headId, $headId, new \DateTime('-2 hours')); + $id = $this->addDatabaseEntry($headId, $headId, new \DateTime('-1 hour')); + + $manager = $this->getManager(); + $tree = $manager->getTree($headId); + + // Verifying the root comment + $this->assertArrayHasKey('comment', $tree); + $this->assertInstanceOf(IComment::class, $tree['comment']); + $this->assertSame((string)$headId, $tree['comment']->getId()); + $this->assertArrayHasKey('replies', $tree); + $this->assertCount(3, $tree['replies']); + + // one level deep + foreach ($tree['replies'] as $reply) { + $this->assertInstanceOf(IComment::class, $reply['comment']); + $this->assertSame((string)$id, $reply['comment']->getId()); + $this->assertCount(0, $reply['replies']); + $id--; + } + } + + public function testGetTreeNoReplies(): void { + $id = $this->addDatabaseEntry(0, 0); + + $manager = $this->getManager(); + $tree = $manager->getTree($id); + + // Verifying the root comment + $this->assertArrayHasKey('comment', $tree); + $this->assertInstanceOf(IComment::class, $tree['comment']); + $this->assertSame((string)$id, $tree['comment']->getId()); + $this->assertArrayHasKey('replies', $tree); + $this->assertCount(0, $tree['replies']); + } + + public function testGetTreeWithLimitAndOffset(): void { + $headId = $this->addDatabaseEntry(0, 0); + + $this->addDatabaseEntry($headId, $headId, new \DateTime('-3 hours')); + $this->addDatabaseEntry($headId, $headId, new \DateTime('-2 hours')); + $this->addDatabaseEntry($headId, $headId, new \DateTime('-1 hour')); + $idToVerify = $this->addDatabaseEntry($headId, $headId, new \DateTime()); + + $manager = $this->getManager(); + + for ($offset = 0; $offset < 3; $offset += 2) { + $tree = $manager->getTree((string)$headId, 2, $offset); + + // Verifying the root comment + $this->assertArrayHasKey('comment', $tree); + $this->assertInstanceOf(IComment::class, $tree['comment']); + $this->assertSame((string)$headId, $tree['comment']->getId()); + $this->assertArrayHasKey('replies', $tree); + $this->assertCount(2, $tree['replies']); + + // one level deep + foreach ($tree['replies'] as $reply) { + $this->assertInstanceOf(IComment::class, $reply['comment']); + $this->assertSame((string)$idToVerify, $reply['comment']->getId()); + $this->assertCount(0, $reply['replies']); + $idToVerify--; + } + } + } + + public function testGetForObject(): void { + $this->addDatabaseEntry(0, 0); + + $manager = $this->getManager(); + $comments = $manager->getForObject('files', 'file64'); + + $this->assertIsArray($comments); + $this->assertCount(1, $comments); + $this->assertInstanceOf(IComment::class, $comments[0]); + $this->assertSame('nice one', $comments[0]->getMessage()); + } + + public function testGetForObjectWithLimitAndOffset(): void { + $this->addDatabaseEntry(0, 0, new \DateTime('-6 hours')); + $this->addDatabaseEntry(0, 0, new \DateTime('-5 hours')); + $this->addDatabaseEntry(1, 1, new \DateTime('-4 hours')); + $this->addDatabaseEntry(0, 0, new \DateTime('-3 hours')); + $this->addDatabaseEntry(2, 2, new \DateTime('-2 hours')); + $this->addDatabaseEntry(2, 2, new \DateTime('-1 hours')); + $idToVerify = $this->addDatabaseEntry(3, 1, new \DateTime()); + + $manager = $this->getManager(); + $offset = 0; + do { + $comments = $manager->getForObject('files', 'file64', 3, $offset); + + $this->assertIsArray($comments); + foreach ($comments as $key => $comment) { + $this->assertInstanceOf(IComment::class, $comment); + $this->assertSame('nice one', $comment->getMessage()); + $this->assertSame((string)$idToVerify, $comment->getId(), 'ID wrong for comment ' . $key . ' on offset: ' . $offset); + $idToVerify--; + } + $offset += 3; + } while (count($comments) > 0); + } + + public function testGetForObjectWithDateTimeConstraint(): void { + $this->addDatabaseEntry(0, 0, new \DateTime('-6 hours')); + $this->addDatabaseEntry(0, 0, new \DateTime('-5 hours')); + $id1 = $this->addDatabaseEntry(0, 0, new \DateTime('-3 hours')); + $id2 = $this->addDatabaseEntry(2, 2, new \DateTime('-2 hours')); + + $manager = $this->getManager(); + $comments = $manager->getForObject('files', 'file64', 0, 0, new \DateTime('-4 hours')); + + $this->assertCount(2, $comments); + $this->assertSame((string)$id2, $comments[0]->getId()); + $this->assertSame((string)$id1, $comments[1]->getId()); + } + + public function testGetForObjectWithLimitAndOffsetAndDateTimeConstraint(): void { + $this->addDatabaseEntry(0, 0, new \DateTime('-7 hours')); + $this->addDatabaseEntry(0, 0, new \DateTime('-6 hours')); + $this->addDatabaseEntry(1, 1, new \DateTime('-5 hours')); + $this->addDatabaseEntry(0, 0, new \DateTime('-3 hours')); + $this->addDatabaseEntry(2, 2, new \DateTime('-2 hours')); + $this->addDatabaseEntry(2, 2, new \DateTime('-1 hours')); + $idToVerify = $this->addDatabaseEntry(3, 1, new \DateTime()); + + $manager = $this->getManager(); + $offset = 0; + do { + $comments = $manager->getForObject('files', 'file64', 3, $offset, new \DateTime('-4 hours')); + + $this->assertIsArray($comments); + foreach ($comments as $comment) { + $this->assertInstanceOf(IComment::class, $comment); + $this->assertSame('nice one', $comment->getMessage()); + $this->assertSame((string)$idToVerify, $comment->getId()); + $this->assertGreaterThanOrEqual(4, $comment->getId()); + $idToVerify--; + } + $offset += 3; + } while (count($comments) > 0); + } + + public function testGetNumberOfCommentsForObject(): void { + for ($i = 1; $i < 5; $i++) { + $this->addDatabaseEntry(0, 0); + } + + $manager = $this->getManager(); + + $amount = $manager->getNumberOfCommentsForObject('untype', '00'); + $this->assertSame(0, $amount); + + $amount = $manager->getNumberOfCommentsForObject('files', 'file64'); + $this->assertSame(4, $amount); + } + + public function testGetNumberOfUnreadCommentsForFolder(): void { + $folder = $this->createMock(Folder::class); + $fileIds = range(1111, 1114); + $children = array_map(function (int $id) { + $file = $this->createMock(Folder::class); + $file->method('getId') + ->willReturn($id); + return $file; + }, $fileIds); + $folder->method('getId')->willReturn(1000); + $folder->method('getDirectoryListing')->willReturn($children); + $this->rootFolder->method('getFirstNodeById') + ->with($folder->getId()) + ->willReturn($folder); + + // 2 comment for 1111 with 1 before read marker + // 2 comments for 1112 with no read marker + // 1 comment for 1113 before read marker + // 1 comment for 1114 with no read marker + $this->addDatabaseEntry(0, 0, null, null, $fileIds[1]); + for ($i = 0; $i < 4; $i++) { + $this->addDatabaseEntry(0, 0, null, null, $fileIds[$i]); + } + $this->addDatabaseEntry(0, 0, (new \DateTime())->modify('-2 days'), null, $fileIds[0]); + /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */ + $user = $this->createMock(IUser::class); + $user->expects($this->any()) + ->method('getUID') + ->willReturn('comment_test'); + + $manager = $this->getManager(); + + $manager->setReadMark('files', (string)$fileIds[0], (new \DateTime())->modify('-1 days'), $user); + $manager->setReadMark('files', (string)$fileIds[2], (new \DateTime()), $user); + + $amount = $manager->getNumberOfUnreadCommentsForFolder($folder->getId(), $user); + $this->assertEquals([ + $fileIds[0] => 1, + $fileIds[1] => 2, + $fileIds[3] => 1, + ], $amount); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('dataGetForObjectSince')] + public function testGetForObjectSince(?int $lastKnown, string $order, int $limit, int $resultFrom, int $resultTo): void { + $ids = []; + $ids[] = $this->addDatabaseEntry(0, 0); + $ids[] = $this->addDatabaseEntry(0, 0); + $ids[] = $this->addDatabaseEntry(0, 0); + $ids[] = $this->addDatabaseEntry(0, 0); + $ids[] = $this->addDatabaseEntry(0, 0); + + $manager = $this->getManager(); + $comments = $manager->getForObjectSince('files', 'file64', ($lastKnown === null ? 0 : $ids[$lastKnown]), $order, $limit); + + $expected = array_slice($ids, $resultFrom, $resultTo - $resultFrom + 1); + if ($order === 'desc') { + $expected = array_reverse($expected); + } + + $this->assertSame($expected, array_map(static fn (IComment $c): int => (int)$c->getId(), $comments)); + } + + public static function dataGetForObjectSince(): array { + return [ + [null, 'asc', 20, 0, 4], + [null, 'asc', 2, 0, 1], + [null, 'desc', 20, 0, 4], + [null, 'desc', 2, 3, 4], + [1, 'asc', 20, 2, 4], + [1, 'asc', 2, 2, 3], + [3, 'desc', 20, 0, 2], + [3, 'desc', 2, 1, 2], + ]; + } + + public static function invalidCreateArgsProvider(): array { + return [ + ['', 'aId-1', 'oType-1', 'oId-1'], + ['aType-1', '', 'oType-1', 'oId-1'], + ['aType-1', 'aId-1', '', 'oId-1'], + ['aType-1', 'aId-1', 'oType-1', ''], + [1, 'aId-1', 'oType-1', 'oId-1'], + ['aType-1', 1, 'oType-1', 'oId-1'], + ['aType-1', 'aId-1', 1, 'oId-1'], + ['aType-1', 'aId-1', 'oType-1', 1], + ]; + } + + #[\PHPUnit\Framework\Attributes\DataProvider('invalidCreateArgsProvider')] + public function testCreateCommentInvalidArguments(string|int $aType, string|int $aId, string|int $oType, string|int $oId): void { + $this->expectException(\InvalidArgumentException::class); + + $manager = $this->getManager(); + $manager->create($aType, $aId, $oType, $oId); + } + + public function testCreateComment(): void { + $actorType = 'bot'; + $actorId = 'bob'; + $objectType = 'weather'; + $objectId = 'bielefeld'; + + $comment = $this->getManager()->create($actorType, $actorId, $objectType, $objectId); + $this->assertInstanceOf(IComment::class, $comment); + $this->assertSame($actorType, $comment->getActorType()); + $this->assertSame($actorId, $comment->getActorId()); + $this->assertSame($objectType, $comment->getObjectType()); + $this->assertSame($objectId, $comment->getObjectId()); + } + + + public function testDelete(): void { + $this->expectException(NotFoundException::class); + + $manager = $this->getManager(); + + $done = $manager->delete('404'); + $this->assertFalse($done); + + $done = $manager->delete('%'); + $this->assertFalse($done); + + $done = $manager->delete(''); + $this->assertFalse($done); + + $id = (string)$this->addDatabaseEntry(0, 0); + $comment = $manager->get($id); + $this->assertInstanceOf(IComment::class, $comment); + $done = $manager->delete($id); + $this->assertTrue($done); + $manager->get($id); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('providerTestSave')] + public function testSave(string $message, string $actorId, string $verb, ?string $parentId, ?string $id = ''): IComment { + $manager = $this->getManager(); + $comment = new Comment(); + $comment + ->setId($id) + ->setActor('users', $actorId) + ->setObject('files', 'file64') + ->setMessage($message) + ->setVerb($verb); + if ($parentId) { + $comment->setParentId($parentId); + } + + $saveSuccessful = $manager->save($comment); + $this->assertTrue($saveSuccessful, 'Comment saving was not successful'); + $this->assertNotEquals('', $comment->getId(), 'Comment ID should not be empty'); + $this->assertNotEquals('0', $comment->getId(), 'Comment ID should not be string \'0\''); + $this->assertNotNull($comment->getCreationDateTime(), 'Comment creation date should not be null'); + + $loadedComment = $manager->get($comment->getId()); + $this->assertSame($comment->getMessage(), $loadedComment->getMessage(), 'Comment message should match'); + $this->assertEquals($comment->getCreationDateTime()->getTimestamp(), $loadedComment->getCreationDateTime()->getTimestamp(), 'Comment creation date should match'); + return $comment; + } + + public static function providerTestSave(): array { + return [ + ['very beautiful, I am impressed!', 'alice', 'comment', null], + ]; + } + + public function testSaveUpdate(): void { + $manager = $this->getManager(); + $comment = new Comment(); + $comment + ->setActor('users', 'alice') + ->setObject('files', 'file64') + ->setMessage('very beautiful, I am impressed!') + ->setVerb('comment') + ->setExpireDate(new \DateTime('+2 hours')); + + $manager->save($comment); + + $loadedComment = $manager->get($comment->getId()); + // Compare current object with database values + $this->assertSame($comment->getMessage(), $loadedComment->getMessage()); + $this->assertSame( + $comment->getExpireDate()->format('Y-m-d H:i:s'), + $loadedComment->getExpireDate()->format('Y-m-d H:i:s') + ); + + // Preserve the original comment to compare after update + $original = clone $comment; + + // Update values + $comment->setMessage('very beautiful, I am really so much impressed!') + ->setExpireDate(new \DateTime('+1 hours')); + $manager->save($comment); + + $loadedComment = $manager->get($comment->getId()); + // Compare current object with database values + $this->assertSame($comment->getMessage(), $loadedComment->getMessage()); + $this->assertSame( + $comment->getExpireDate()->format('Y-m-d H:i:s'), + $loadedComment->getExpireDate()->format('Y-m-d H:i:s') + ); + + // Compare original object with database values + $this->assertNotSame($original->getMessage(), $loadedComment->getMessage()); + $this->assertNotSame( + $original->getExpireDate()->format('Y-m-d H:i:s'), + $loadedComment->getExpireDate()->format('Y-m-d H:i:s') + ); + } + + + public function testSaveUpdateException(): void { + $manager = $this->getManager(); + $comment = new Comment(); + $comment + ->setActor('users', 'alice') + ->setObject('files', 'file64') + ->setMessage('very beautiful, I am impressed!') + ->setVerb('comment'); + + $manager->save($comment); + + $manager->delete($comment->getId()); + + $comment->setMessage('very beautiful, I am really so much impressed!'); + $this->expectException(NotFoundException::class); + $manager->save($comment); + } + + + public function testSaveIncomplete(): void { + + $manager = $this->getManager(); + $comment = new Comment(); + $comment->setMessage('from no one to nothing'); + + $this->expectException(\UnexpectedValueException::class); + $manager->save($comment); + } + + public function testSaveAsChild(): void { + $id = (string)$this->addDatabaseEntry(0, 0); + + $manager = $this->getManager(); + + for ($i = 0; $i < 3; $i++) { + $comment = new Comment(); + $comment + ->setActor('users', 'alice') + ->setObject('files', 'file64') + ->setParentId($id) + ->setMessage('full ack') + ->setVerb('comment') + // setting the creation time avoids using sleep() while making sure to test with different timestamps + ->setCreationDateTime(new \DateTime('+' . $i . ' minutes')); + + $manager->save($comment); + + $this->assertSame($id, $comment->getTopmostParentId()); + $parentComment = $manager->get($id); + $this->assertSame($i + 1, $parentComment->getChildrenCount()); + $this->assertEquals($comment->getCreationDateTime()->getTimestamp(), $parentComment->getLatestChildDateTime()->getTimestamp()); + } + } + + public static function invalidActorArgsProvider(): array { + return + [ + ['', ''], + [1, 'alice'], + ['users', 1], + ]; + } + + #[\PHPUnit\Framework\Attributes\DataProvider('invalidActorArgsProvider')] + public function testDeleteReferencesOfActorInvalidInput(string|int $type, string|int $id): void { + $this->expectException(\InvalidArgumentException::class); + + $manager = $this->getManager(); + $manager->deleteReferencesOfActor($type, $id); + } + + public function testDeleteReferencesOfActor(): void { + $ids = []; + $ids[] = $this->addDatabaseEntry(0, 0); + $ids[] = $this->addDatabaseEntry(0, 0); + $ids[] = $this->addDatabaseEntry(0, 0); + + $manager = $this->getManager(); + + // just to make sure they are really set, with correct actor data + $comment = $manager->get((string)$ids[1]); + $this->assertSame('users', $comment->getActorType()); + $this->assertSame('alice', $comment->getActorId()); + + $wasSuccessful = $manager->deleteReferencesOfActor('users', 'alice'); + $this->assertTrue($wasSuccessful); + + foreach ($ids as $id) { + $comment = $manager->get((string)$id); + $this->assertSame(ICommentsManager::DELETED_USER, $comment->getActorType()); + $this->assertSame(ICommentsManager::DELETED_USER, $comment->getActorId()); + } + + // actor info is gone from DB, but when database interaction is alright, + // we still expect to get true back + $wasSuccessful = $manager->deleteReferencesOfActor('users', 'alice'); + $this->assertTrue($wasSuccessful); + } + + public function testDeleteReferencesOfActorWithUserManagement(): void { + $user = Server::get(IUserManager::class)->createUser('xenia', 'NotAnEasyPassword123456+'); + $this->assertInstanceOf(IUser::class, $user); + + $manager = Server::get(ICommentsManager::class); + $comment = $manager->create('users', $user->getUID(), 'files', 'file64'); + $comment + ->setMessage('Most important comment I ever left on the Internet.') + ->setVerb('comment'); + $status = $manager->save($comment); + $this->assertTrue($status); + + $commentID = $comment->getId(); + $user->delete(); + + $comment = $manager->get($commentID); + $this->assertSame(ICommentsManager::DELETED_USER, $comment->getActorType()); + $this->assertSame(ICommentsManager::DELETED_USER, $comment->getActorId()); + } + + public static function invalidObjectArgsProvider(): array { + return + [ + ['', ''], + [1, 'file64'], + ['files', 1], + ]; + } + + #[\PHPUnit\Framework\Attributes\DataProvider('invalidObjectArgsProvider')] + public function testDeleteCommentsAtObjectInvalidInput(string|int $type, string|int $id): void { + $this->expectException(\InvalidArgumentException::class); + + $manager = $this->getManager(); + $manager->deleteCommentsAtObject($type, $id); + } + + public function testDeleteCommentsAtObject(): void { + $ids = []; + $ids[] = $this->addDatabaseEntry(0, 0); + $ids[] = $this->addDatabaseEntry(0, 0); + $ids[] = $this->addDatabaseEntry(0, 0); + + $manager = $this->getManager(); + + // just to make sure they are really set, with correct actor data + $comment = $manager->get((string)$ids[1]); + $this->assertSame('files', $comment->getObjectType()); + $this->assertSame('file64', $comment->getObjectId()); + + $wasSuccessful = $manager->deleteCommentsAtObject('files', 'file64'); + $this->assertTrue($wasSuccessful); + + $verified = 0; + foreach ($ids as $id) { + try { + $manager->get((string)$id); + } catch (NotFoundException) { + $verified++; + } + } + $this->assertSame(3, $verified); + + // actor info is gone from DB, but when database interaction is alright, + // we still expect to get true back + $wasSuccessful = $manager->deleteCommentsAtObject('files', 'file64'); + $this->assertTrue($wasSuccessful); + } + + public function testDeleteCommentsExpiredAtObjectTypeAndId(): void { + $ids = []; + $ids[] = $this->addDatabaseEntry(0, 0, null, null, null, new \DateTime('+2 hours')); + $ids[] = $this->addDatabaseEntry(0, 0, null, null, null, new \DateTime('+2 hours')); + $ids[] = $this->addDatabaseEntry(0, 0, null, null, null, new \DateTime('+2 hours')); + $ids[] = $this->addDatabaseEntry(0, 0, null, null, null, new \DateTime('-2 hours')); + $ids[] = $this->addDatabaseEntry(0, 0, null, null, null, new \DateTime('-2 hours')); + $ids[] = $this->addDatabaseEntry(0, 0, null, null, null, new \DateTime('-2 hours')); + + $manager = new Manager( + $this->connection, + $this->createMock(LoggerInterface::class), + $this->createMock(IConfig::class), + Server::get(ITimeFactory::class), + new EmojiHelper($this->connection), + $this->createMock(IInitialStateService::class), + $this->rootFolder, + $this->createMock(IEventDispatcher::class) + ); + + // just to make sure they are really set, with correct actor data + $comment = $manager->get((string)$ids[1]); + $this->assertSame('files', $comment->getObjectType()); + $this->assertSame('file64', $comment->getObjectId()); + + $deleted = $manager->deleteCommentsExpiredAtObject('files', 'file64'); + $this->assertTrue($deleted); + + $deleted = 0; + $exists = 0; + foreach ($ids as $id) { + try { + $manager->get((string)$id); + $exists++; + } catch (NotFoundException) { + $deleted++; + } + } + $this->assertSame(3, $exists); + $this->assertSame(3, $deleted); + + // actor info is gone from DB, but when database interaction is alright, + // we still expect to get true back + $deleted = $manager->deleteCommentsExpiredAtObject('files', 'file64'); + $this->assertFalse($deleted); + } + + public function testDeleteCommentsExpiredAtObjectType(): void { + $ids = []; + $ids[] = $this->addDatabaseEntry(0, 0, null, null, 'file1', new \DateTime('-2 hours')); + $ids[] = $this->addDatabaseEntry(0, 0, null, null, 'file2', new \DateTime('-2 hours')); + $ids[] = $this->addDatabaseEntry(0, 0, null, null, 'file3', new \DateTime('-2 hours')); + $ids[] = $this->addDatabaseEntry(0, 0, null, null, 'file3', new \DateTime()); + $ids[] = $this->addDatabaseEntry(0, 0, null, null, 'file3', new \DateTime()); + $ids[] = $this->addDatabaseEntry(0, 0, null, null, 'file3', new \DateTime()); + + $manager = new Manager( + $this->connection, + $this->createMock(LoggerInterface::class), + $this->createMock(IConfig::class), + Server::get(ITimeFactory::class), + new EmojiHelper($this->connection), + $this->createMock(IInitialStateService::class), + $this->rootFolder, + $this->createMock(IEventDispatcher::class) + ); + + $deleted = $manager->deleteCommentsExpiredAtObject('files'); + $this->assertTrue($deleted); + + $deleted = 0; + $exists = 0; + foreach ($ids as $id) { + try { + $manager->get((string)$id); + $exists++; + } catch (NotFoundException) { + $deleted++; + } + } + $this->assertSame(0, $exists); + $this->assertSame(6, $deleted); + + // actor info is gone from DB, but when database interaction is alright, + // we still expect to get true back + $deleted = $manager->deleteCommentsExpiredAtObject('files'); + $this->assertFalse($deleted); + } + + public function testSetMarkRead(): void { + /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */ + $user = $this->createMock(IUser::class); + $user->expects($this->any()) + ->method('getUID') + ->willReturn('alice'); + + $dateTimeSet = new \DateTime(); + + $manager = $this->getManager(); + $manager->setReadMark('robot', '36', $dateTimeSet, $user); + + $dateTimeGet = $manager->getReadMark('robot', '36', $user); + + $this->assertEquals($dateTimeSet->getTimestamp(), $dateTimeGet->getTimestamp()); + } + + public function testSetMarkReadUpdate(): void { + /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */ + $user = $this->createMock(IUser::class); + $user->expects($this->any()) + ->method('getUID') + ->willReturn('alice'); + + $dateTimeSet = new \DateTime('yesterday'); + + $manager = $this->getManager(); + $manager->setReadMark('robot', '36', $dateTimeSet, $user); + + $dateTimeSet = new \DateTime('today'); + $manager->setReadMark('robot', '36', $dateTimeSet, $user); + + $dateTimeGet = $manager->getReadMark('robot', '36', $user); + + $this->assertEquals($dateTimeSet, $dateTimeGet); + } + + public function testReadMarkDeleteUser(): void { + /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */ + $user = $this->createMock(IUser::class); + $user->expects($this->any()) + ->method('getUID') + ->willReturn('alice'); + + $dateTimeSet = new \DateTime(); + + $manager = $this->getManager(); + $manager->setReadMark('robot', '36', $dateTimeSet, $user); + + $manager->deleteReadMarksFromUser($user); + $dateTimeGet = $manager->getReadMark('robot', '36', $user); + + $this->assertNull($dateTimeGet); + } + + public function testReadMarkDeleteObject(): void { + /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */ + $user = $this->createMock(IUser::class); + $user->expects($this->any()) + ->method('getUID') + ->willReturn('alice'); + + $dateTimeSet = new \DateTime(); + + $manager = $this->getManager(); + $manager->setReadMark('robot', '36', $dateTimeSet, $user); + + $manager->deleteReadMarksOnObject('robot', '36'); + $dateTimeGet = $manager->getReadMark('robot', '36', $user); + + $this->assertNull($dateTimeGet); + } + + public function testSendEvent(): void { + $handler1 = $this->createMock(ICommentsEventHandler::class); + $handler1->expects($this->exactly(4)) + ->method('handle'); + + $handler2 = $this->createMock(ICommentsEventHandler::class); + $handler1->expects($this->exactly(4)) + ->method('handle'); + + $manager = $this->getManager(); + $manager->registerEventHandler(function () use ($handler1) { + return $handler1; + }); + $manager->registerEventHandler(function () use ($handler2) { + return $handler2; + }); + + $comment = new Comment(); + $comment + ->setActor('users', 'alice') + ->setObject('files', 'file64') + ->setMessage('very beautiful, I am impressed!') + ->setVerb('comment'); + + // Add event + $manager->save($comment); + + // Update event + $comment->setMessage('Different topic'); + $manager->save($comment); + + // Delete event + $manager->delete($comment->getId()); + } + + public function testResolveDisplayName(): void { + $manager = $this->getManager(); + + $planetClosure = function ($name) { + return ucfirst($name); + }; + + $galaxyClosure = function ($name) { + return strtoupper($name); + }; + + $manager->registerDisplayNameResolver('planet', $planetClosure); + $manager->registerDisplayNameResolver('galaxy', $galaxyClosure); + + $this->assertSame('Neptune', $manager->resolveDisplayName('planet', 'neptune')); + $this->assertSame('SOMBRERO', $manager->resolveDisplayName('galaxy', 'sombrero')); + } + + + public function testRegisterResolverDuplicate(): void { + $this->expectException(\OutOfBoundsException::class); + + $manager = $this->getManager(); + + $planetClosure = function ($name) { + return ucfirst($name); + }; + $manager->registerDisplayNameResolver('planet', $planetClosure); + $manager->registerDisplayNameResolver('planet', $planetClosure); + } + + + public function testRegisterResolverInvalidType(): void { + $this->expectException(\InvalidArgumentException::class); + + $manager = $this->getManager(); + + $planetClosure = function ($name) { + return ucfirst($name); + }; + $manager->registerDisplayNameResolver(1337, $planetClosure); + } + + + public function testResolveDisplayNameUnregisteredType(): void { + $this->expectException(\OutOfBoundsException::class); + + $manager = $this->getManager(); + + $planetClosure = function ($name) { + return ucfirst($name); + }; + + $manager->registerDisplayNameResolver('planet', $planetClosure); + $manager->resolveDisplayName('galaxy', 'sombrero'); + } + + public function testResolveDisplayNameDirtyResolver(): void { + $manager = $this->getManager(); + + $planetClosure = function () { + return null; + }; + + $manager->registerDisplayNameResolver('planet', $planetClosure); + $this->assertIsString($manager->resolveDisplayName('planet', 'neptune')); + } + + public function testResolveDisplayNameInvalidType(): void { + + $manager = $this->getManager(); + + $planetClosure = function () { + return null; + }; + + $manager->registerDisplayNameResolver('planet', $planetClosure); + $this->expectException(\InvalidArgumentException::class); + $this->assertIsString($manager->resolveDisplayName(1337, 'neptune')); + } + + private function skipIfNotSupport4ByteUTF(): void { + if (!$this->getManager()->supportReactions()) { + $this->markTestSkipped('MySQL doesn\'t support 4 byte UTF-8'); + } + } + + #[\PHPUnit\Framework\Attributes\DataProvider('providerTestReactionAddAndDelete')] + public function testReactionAddAndDelete(array $comments, array $reactionsExpected): void { + $this->skipIfNotSupport4ByteUTF(); + $manager = $this->getManager(); + + $processedComments = $this->proccessComments($comments); + $comment = end($processedComments); + if ($comment->getParentId()) { + $parent = $manager->get($comment->getParentId()); + $this->assertEqualsCanonicalizing($reactionsExpected, $parent->getReactions()); + } + } + + public static function providerTestReactionAddAndDelete(): array { + return[ + [ + [ + ['message', 'alice', 'comment', null], + ], [], + ], + [ + [ + ['message', 'alice', 'comment', null], + ['๐', 'alice', 'reaction', 'message#alice'], + ], ['๐' => 1], + ], + [ + [ + ['message', 'alice', 'comment', null], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ], ['๐' => 1], + ], + [ + [ + ['message', 'alice', 'comment', null], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐', 'frank', 'reaction', 'message#alice'], + ], ['๐' => 2], + ], + [ + [ + ['message', 'alice', 'comment', null], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐', 'frank', 'reaction', 'message#alice'], + ['๐', 'frank', 'reaction_deleted', 'message#alice'], + ], ['๐' => 1], + ], + [ + [ + ['message', 'alice', 'comment', null], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐', 'frank', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction_deleted', 'message#alice'], + ['๐', 'frank', 'reaction_deleted', 'message#alice'], + ], [], + ], + ]; + } + + /** + * @param array $data + * @return array<string, IComment> + */ + private function proccessComments(array $data): array { + $this->connection->beginTransaction(); + /** @var array<string, IComment> $comments */ + $comments = []; + foreach ($data as $comment) { + [$message, $actorId, $verb, $parentText] = $comment; + $parentId = null; + if ($parentText) { + $parentId = (string)$comments[$parentText]->getId(); + } + $id = ''; + if ($verb === 'reaction_deleted') { + $id = $comments[$message . '#' . $actorId]->getId(); + } + $comment = $this->testSave($message, $actorId, $verb, $parentId, $id); + $comments[$comment->getMessage() . '#' . $comment->getActorId()] = $comment; + } + $this->connection->commit(); + return $comments; + } + + #[\PHPUnit\Framework\Attributes\DataProvider('providerTestRetrieveAllReactions')] + public function testRetrieveAllReactions(array $comments, array $expected): void { + $this->skipIfNotSupport4ByteUTF(); + $manager = $this->getManager(); + + $processedComments = $this->proccessComments($comments); + $comment = reset($processedComments); + $all = $manager->retrieveAllReactions((int)$comment->getId()); + $actual = array_map(static function (IComment $row): array { + return [ + $row->getActorId(), + $row->getMessage(), + ]; + }, $all); + + usort($actual, static fn (array $a, array $b): int => $a[1] <=> $b[1]); + usort($expected, static fn (array $a, array $b): int => $a[1] <=> $b[1]); + + $this->assertEqualsCanonicalizing($expected, $actual); + } + + public static function providerTestRetrieveAllReactions(): array { + return [ + [ + [ + ['message', 'alice', 'comment', null], + ], + [], + ], + [ + [ + ['message', 'alice', 'comment', null], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐', 'frank', 'reaction', 'message#alice'], + ], + [ + ['๐', 'alice'], + ['๐', 'frank'], + ], + ], + [ + [ + ['message', 'alice', 'comment', null], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐', 'frank', 'reaction', 'message#alice'], + ], + [ + ['๐', 'alice'], + ['๐', 'frank'], + ], + ], + [# 600 reactions to cover chunk size when retrieve comments of reactions. + [ + ['message', 'alice', 'comment', null], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐
', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐คฃ', 'alice', 'reaction', 'message#alice'], + ['๐ฅฒ', 'alice', 'reaction', 'message#alice'], + ['๐ฅน', 'alice', 'reaction', 'message#alice'], + ['โบ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐ฅฐ', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐คช', 'alice', 'reaction', 'message#alice'], + ['๐คจ', 'alice', 'reaction', 'message#alice'], + ['๐ง', 'alice', 'reaction', 'message#alice'], + ['๐ค', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐ฅธ', 'alice', 'reaction', 'message#alice'], + ['๐คฉ', 'alice', 'reaction', 'message#alice'], + ['๐ฅณ', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['โน๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ฃ', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐ซ', 'alice', 'reaction', 'message#alice'], + ['๐ฉ', 'alice', 'reaction', 'message#alice'], + ['๐ฅบ', 'alice', 'reaction', 'message#alice'], + ['๐ข', 'alice', 'reaction', 'message#alice'], + ['๐ญ', 'alice', 'reaction', 'message#alice'], + ['๐ฎโ๐จ', 'alice', 'reaction', 'message#alice'], + ['๐ค', 'alice', 'reaction', 'message#alice'], + ['๐ ', 'alice', 'reaction', 'message#alice'], + ['๐ก', 'alice', 'reaction', 'message#alice'], + ['๐คฌ', 'alice', 'reaction', 'message#alice'], + ['๐คฏ', 'alice', 'reaction', 'message#alice'], + ['๐ณ', 'alice', 'reaction', 'message#alice'], + ['๐ฅต', 'alice', 'reaction', 'message#alice'], + ['๐ฅถ', 'alice', 'reaction', 'message#alice'], + ['๐ฑ', 'alice', 'reaction', 'message#alice'], + ['๐จ', 'alice', 'reaction', 'message#alice'], + ['๐ฐ', 'alice', 'reaction', 'message#alice'], + ['๐ฅ', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐ซฃ', 'alice', 'reaction', 'message#alice'], + ['๐ค', 'alice', 'reaction', 'message#alice'], + ['๐ซก', 'alice', 'reaction', 'message#alice'], + ['๐ค', 'alice', 'reaction', 'message#alice'], + ['๐ซข', 'alice', 'reaction', 'message#alice'], + ['๐คญ', 'alice', 'reaction', 'message#alice'], + ['๐คซ', 'alice', 'reaction', 'message#alice'], + ['๐คฅ', 'alice', 'reaction', 'message#alice'], + ['๐ถ', 'alice', 'reaction', 'message#alice'], + ['๐ถโ๐ซ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐ฌ', 'alice', 'reaction', 'message#alice'], + ['๐ซ ', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐ฏ', 'alice', 'reaction', 'message#alice'], + ['๐ฆ', 'alice', 'reaction', 'message#alice'], + ['๐ง', 'alice', 'reaction', 'message#alice'], + ['๐ฎ', 'alice', 'reaction', 'message#alice'], + ['๐ฒ', 'alice', 'reaction', 'message#alice'], + ['๐ฅฑ', 'alice', 'reaction', 'message#alice'], + ['๐ด', 'alice', 'reaction', 'message#alice'], + ['๐คค', 'alice', 'reaction', 'message#alice'], + ['๐ช', 'alice', 'reaction', 'message#alice'], + ['๐ต', 'alice', 'reaction', 'message#alice'], + ['๐ตโ๐ซ', 'alice', 'reaction', 'message#alice'], + ['๐ซฅ', 'alice', 'reaction', 'message#alice'], + ['๐ค', 'alice', 'reaction', 'message#alice'], + ['๐ฅด', 'alice', 'reaction', 'message#alice'], + ['๐คข', 'alice', 'reaction', 'message#alice'], + ['๐คฎ', 'alice', 'reaction', 'message#alice'], + ['๐คง', 'alice', 'reaction', 'message#alice'], + ['๐ท', 'alice', 'reaction', 'message#alice'], + ['๐ค', 'alice', 'reaction', 'message#alice'], + ['๐ค', 'alice', 'reaction', 'message#alice'], + ['๐ค', 'alice', 'reaction', 'message#alice'], + ['๐ค ', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐ฟ', 'alice', 'reaction', 'message#alice'], + ['๐น', 'alice', 'reaction', 'message#alice'], + ['๐บ', 'alice', 'reaction', 'message#alice'], + ['๐คก', 'alice', 'reaction', 'message#alice'], + ['๐ฉ', 'alice', 'reaction', 'message#alice'], + ['๐ป', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['โ ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐พ', 'alice', 'reaction', 'message#alice'], + ['๐ค', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐บ', 'alice', 'reaction', 'message#alice'], + ['๐ธ', 'alice', 'reaction', 'message#alice'], + ['๐น', 'alice', 'reaction', 'message#alice'], + ['๐ป', 'alice', 'reaction', 'message#alice'], + ['๐ผ', 'alice', 'reaction', 'message#alice'], + ['๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐ฟ', 'alice', 'reaction', 'message#alice'], + ['๐พ', 'alice', 'reaction', 'message#alice'], + ['๐ถ', 'alice', 'reaction', 'message#alice'], + ['๐ง', 'alice', 'reaction', 'message#alice'], + ['๐ง', 'alice', 'reaction', 'message#alice'], + ['๐ฆ', 'alice', 'reaction', 'message#alice'], + ['๐ฉ', 'alice', 'reaction', 'message#alice'], + ['๐ง', 'alice', 'reaction', 'message#alice'], + ['๐จ', 'alice', 'reaction', 'message#alice'], + ['๐ฉโ๐ฆฑ', 'alice', 'reaction', 'message#alice'], + ['๐งโ๐ฆฑ', 'alice', 'reaction', 'message#alice'], + ['๐จโ๐ฆฑ', 'alice', 'reaction', 'message#alice'], + ['๐ฉโ๐ฆฐ', 'alice', 'reaction', 'message#alice'], + ['๐งโ๐ฆฐ', 'alice', 'reaction', 'message#alice'], + ['๐จโ๐ฆฐ', 'alice', 'reaction', 'message#alice'], + ['๐ฑโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ฑ', 'alice', 'reaction', 'message#alice'], + ['๐ฑโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ฉโ๐ฆณ', 'alice', 'reaction', 'message#alice'], + ['๐งโ๐ฆณ', 'alice', 'reaction', 'message#alice'], + ['๐จโ๐ฆณ', 'alice', 'reaction', 'message#alice'], + ['๐ฉโ๐ฆฒ', 'alice', 'reaction', 'message#alice'], + ['๐งโ๐ฆฒ', 'alice', 'reaction', 'message#alice'], + ['๐จโ๐ฆฒ', 'alice', 'reaction', 'message#alice'], + ['๐งโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ง', 'alice', 'reaction', 'message#alice'], + ['๐งโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ต', 'alice', 'reaction', 'message#alice'], + ['๐ง', 'alice', 'reaction', 'message#alice'], + ['๐ด', 'alice', 'reaction', 'message#alice'], + ['๐ฒ', 'alice', 'reaction', 'message#alice'], + ['๐ณโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ณ', 'alice', 'reaction', 'message#alice'], + ['๐ณโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ง', 'alice', 'reaction', 'message#alice'], + ['๐ฎโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ฎ', 'alice', 'reaction', 'message#alice'], + ['๐ฎโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ทโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ท', 'alice', 'reaction', 'message#alice'], + ['๐ทโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐โโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐โโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ต๏ธโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ต๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ต๏ธโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ฉโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐งโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐จโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ฉโ๐พ', 'alice', 'reaction', 'message#alice'], + ['๐งโ๐พ', 'alice', 'reaction', 'message#alice'], + ['๐จโ๐พ', 'alice', 'reaction', 'message#alice'], + ['๐ฉโ๐ณ', 'alice', 'reaction', 'message#alice'], + ['๐งโ๐ณ', 'alice', 'reaction', 'message#alice'], + ['๐จโ๐ณ', 'alice', 'reaction', 'message#alice'], + ['๐ฉโ๐', 'alice', 'reaction', 'message#alice'], + ['๐งโ๐', 'alice', 'reaction', 'message#alice'], + ['๐จโ๐', 'alice', 'reaction', 'message#alice'], + ['๐ฉโ๐ค', 'alice', 'reaction', 'message#alice'], + ['๐งโ๐ค', 'alice', 'reaction', 'message#alice'], + ['๐จโ๐ค', 'alice', 'reaction', 'message#alice'], + ['๐ฉโ๐ซ', 'alice', 'reaction', 'message#alice'], + ['๐งโ๐ซ', 'alice', 'reaction', 'message#alice'], + ['๐จโ๐ซ', 'alice', 'reaction', 'message#alice'], + ['๐ฉโ๐ญ', 'alice', 'reaction', 'message#alice'], + ['๐งโ๐ญ', 'alice', 'reaction', 'message#alice'], + ['๐จโ๐ญ', 'alice', 'reaction', 'message#alice'], + ['๐ฉโ๐ป', 'alice', 'reaction', 'message#alice'], + ['๐งโ๐ป', 'alice', 'reaction', 'message#alice'], + ['๐จโ๐ป', 'alice', 'reaction', 'message#alice'], + ['๐ฉโ๐ผ', 'alice', 'reaction', 'message#alice'], + ['๐งโ๐ผ', 'alice', 'reaction', 'message#alice'], + ['๐จโ๐ผ', 'alice', 'reaction', 'message#alice'], + ['๐ฉโ๐ง', 'alice', 'reaction', 'message#alice'], + ['๐งโ๐ง', 'alice', 'reaction', 'message#alice'], + ['๐จโ๐ง', 'alice', 'reaction', 'message#alice'], + ['๐ฉโ๐ฌ', 'alice', 'reaction', 'message#alice'], + ['๐งโ๐ฌ', 'alice', 'reaction', 'message#alice'], + ['๐จโ๐ฌ', 'alice', 'reaction', 'message#alice'], + ['๐ฉโ๐จ', 'alice', 'reaction', 'message#alice'], + ['๐งโ๐จ', 'alice', 'reaction', 'message#alice'], + ['๐จโ๐จ', 'alice', 'reaction', 'message#alice'], + ['๐ฉโ๐', 'alice', 'reaction', 'message#alice'], + ['๐งโ๐', 'alice', 'reaction', 'message#alice'], + ['๐จโ๐', 'alice', 'reaction', 'message#alice'], + ['๐ฉโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐งโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐จโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ฉโ๐', 'alice', 'reaction', 'message#alice'], + ['๐งโ๐', 'alice', 'reaction', 'message#alice'], + ['๐จโ๐', 'alice', 'reaction', 'message#alice'], + ['๐ฉโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐งโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐จโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ฐโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ฐ', 'alice', 'reaction', 'message#alice'], + ['๐ฐโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐คตโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐คต', 'alice', 'reaction', 'message#alice'], + ['๐คตโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ธ', 'alice', 'reaction', 'message#alice'], + ['๐ซ
', 'alice', 'reaction', 'message#alice'], + ['๐คด', 'alice', 'reaction', 'message#alice'], + ['๐ฅท', 'alice', 'reaction', 'message#alice'], + ['๐ฆธโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ฆธ', 'alice', 'reaction', 'message#alice'], + ['๐ฆธโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ฆนโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ฆน', 'alice', 'reaction', 'message#alice'], + ['๐ฆนโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐คถ', 'alice', 'reaction', 'message#alice'], + ['๐งโ๐', 'alice', 'reaction', 'message#alice'], + ['๐
', 'alice', 'reaction', 'message#alice'], + ['๐งโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ง', 'alice', 'reaction', 'message#alice'], + ['๐งโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐งโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ง', 'alice', 'reaction', 'message#alice'], + ['๐งโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐งโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ง', 'alice', 'reaction', 'message#alice'], + ['๐งโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐งโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ง', 'alice', 'reaction', 'message#alice'], + ['๐งโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐งโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ง', 'alice', 'reaction', 'message#alice'], + ['๐งโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐งโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ง', 'alice', 'reaction', 'message#alice'], + ['๐งโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐งโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ง', 'alice', 'reaction', 'message#alice'], + ['๐งโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ง', 'alice', 'reaction', 'message#alice'], + ['๐ผ', 'alice', 'reaction', 'message#alice'], + ['๐คฐ', 'alice', 'reaction', 'message#alice'], + ['๐ซ', 'alice', 'reaction', 'message#alice'], + ['๐ซ', 'alice', 'reaction', 'message#alice'], + ['๐คฑ', 'alice', 'reaction', 'message#alice'], + ['๐ฉโ๐ผ', 'alice', 'reaction', 'message#alice'], + ['๐งโ๐ผ', 'alice', 'reaction', 'message#alice'], + ['๐จโ๐ผ', 'alice', 'reaction', 'message#alice'], + ['๐โโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐โโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐โโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐โโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐
โโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐
', 'alice', 'reaction', 'message#alice'], + ['๐
โโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐โโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐โโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐โโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐โโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐งโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ง', 'alice', 'reaction', 'message#alice'], + ['๐งโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐คฆโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐คฆ', 'alice', 'reaction', 'message#alice'], + ['๐คฆโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐คทโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐คท', 'alice', 'reaction', 'message#alice'], + ['๐คทโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐โโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐โโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐โโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐โโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐โโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐โโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐โโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐โโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐งโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ง', 'alice', 'reaction', 'message#alice'], + ['๐งโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐
', 'alice', 'reaction', 'message#alice'], + ['๐คณ', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐บ', 'alice', 'reaction', 'message#alice'], + ['๐ฏโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ฏ', 'alice', 'reaction', 'message#alice'], + ['๐ฏโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ด', 'alice', 'reaction', 'message#alice'], + ['๐ฉโ๐ฆฝ', 'alice', 'reaction', 'message#alice'], + ['๐งโ๐ฆฝ', 'alice', 'reaction', 'message#alice'], + ['๐จโ๐ฆฝ', 'alice', 'reaction', 'message#alice'], + ['๐ฉโ๐ฆผ', 'alice', 'reaction', 'message#alice'], + ['๐งโ๐ฆผ', 'alice', 'reaction', 'message#alice'], + ['๐จโ๐ฆผ', 'alice', 'reaction', 'message#alice'], + ['๐ถโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ถ', 'alice', 'reaction', 'message#alice'], + ['๐ถโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ฉโ๐ฆฏ', 'alice', 'reaction', 'message#alice'], + ['๐งโ๐ฆฏ', 'alice', 'reaction', 'message#alice'], + ['๐จโ๐ฆฏ', 'alice', 'reaction', 'message#alice'], + ['๐งโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ง', 'alice', 'reaction', 'message#alice'], + ['๐งโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐โโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐โโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐งโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ง', 'alice', 'reaction', 'message#alice'], + ['๐งโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ญ', 'alice', 'reaction', 'message#alice'], + ['๐งโ๐คโ๐ง', 'alice', 'reaction', 'message#alice'], + ['๐ฌ', 'alice', 'reaction', 'message#alice'], + ['๐ซ', 'alice', 'reaction', 'message#alice'], + ['๐ฉโโค๏ธโ๐ฉ', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐จโโค๏ธโ๐จ', 'alice', 'reaction', 'message#alice'], + ['๐ฉโโค๏ธโ๐จ', 'alice', 'reaction', 'message#alice'], + ['๐ฉโโค๏ธโ๐โ๐ฉ', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐จโโค๏ธโ๐โ๐จ', 'alice', 'reaction', 'message#alice'], + ['๐ฉโโค๏ธโ๐โ๐จ', 'alice', 'reaction', 'message#alice'], + ['๐ช', 'alice', 'reaction', 'message#alice'], + ['๐จโ๐ฉโ๐ฆ', 'alice', 'reaction', 'message#alice'], + ['๐จโ๐ฉโ๐ง', 'alice', 'reaction', 'message#alice'], + ['๐จโ๐ฉโ๐งโ๐ฆ', 'alice', 'reaction', 'message#alice'], + ['๐จโ๐ฉโ๐ฆโ๐ฆ', 'alice', 'reaction', 'message#alice'], + ['๐จโ๐ฉโ๐งโ๐ง', 'alice', 'reaction', 'message#alice'], + ['๐จโ๐จโ๐ฆ', 'alice', 'reaction', 'message#alice'], + ['๐จโ๐จโ๐ง', 'alice', 'reaction', 'message#alice'], + ['๐จโ๐จโ๐งโ๐ฆ', 'alice', 'reaction', 'message#alice'], + ['๐จโ๐จโ๐ฆโ๐ฆ', 'alice', 'reaction', 'message#alice'], + ['๐จโ๐จโ๐งโ๐ง', 'alice', 'reaction', 'message#alice'], + ['๐ฉโ๐ฉโ๐ฆ', 'alice', 'reaction', 'message#alice'], + ['๐ฉโ๐ฉโ๐ง', 'alice', 'reaction', 'message#alice'], + ['๐ฉโ๐ฉโ๐งโ๐ฆ', 'alice', 'reaction', 'message#alice'], + ['๐ฉโ๐ฉโ๐ฆโ๐ฆ', 'alice', 'reaction', 'message#alice'], + ['๐ฉโ๐ฉโ๐งโ๐ง', 'alice', 'reaction', 'message#alice'], + ['๐จโ๐ฆ', 'alice', 'reaction', 'message#alice'], + ['๐จโ๐ฆโ๐ฆ', 'alice', 'reaction', 'message#alice'], + ['๐จโ๐ง', 'alice', 'reaction', 'message#alice'], + ['๐จโ๐งโ๐ฆ', 'alice', 'reaction', 'message#alice'], + ['๐จโ๐งโ๐ง', 'alice', 'reaction', 'message#alice'], + ['๐ฉโ๐ฆ', 'alice', 'reaction', 'message#alice'], + ['๐ฉโ๐ฆโ๐ฆ', 'alice', 'reaction', 'message#alice'], + ['๐ฉโ๐ง', 'alice', 'reaction', 'message#alice'], + ['๐ฉโ๐งโ๐ฆ', 'alice', 'reaction', 'message#alice'], + ['๐ฉโ๐งโ๐ง', 'alice', 'reaction', 'message#alice'], + ['๐ฃ', 'alice', 'reaction', 'message#alice'], + ['๐ค', 'alice', 'reaction', 'message#alice'], + ['๐ฅ', 'alice', 'reaction', 'message#alice'], + ['๐ซ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ค๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝ', 'alice', 'reaction', 'message#alice'], + ['โ๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ค๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ค๐ฝ', 'alice', 'reaction', 'message#alice'], + ['โ๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ค๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ซฐ๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ค๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ค๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ค๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ซต๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ซฑ๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ซฒ๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ซณ๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ซด๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝ', 'alice', 'reaction', 'message#alice'], + ['โ๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝ', 'alice', 'reaction', 'message#alice'], + ['โ๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ค๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ค๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ซถ๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐คฒ๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝ', 'alice', 'reaction', 'message#alice'], + ['โ๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐
๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐คณ๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ช๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ฆต๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ฆถ๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ฆป๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ถ๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ฆ๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ฉ๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐จ๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ฉ๐ฝโ๐ฆฑ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝโ๐ฆฑ', 'alice', 'reaction', 'message#alice'], + ['๐จ๐ฝโ๐ฆฑ', 'alice', 'reaction', 'message#alice'], + ['๐ฉ๐ฝโ๐ฆฐ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝโ๐ฆฐ', 'alice', 'reaction', 'message#alice'], + ['๐จ๐ฝโ๐ฆฐ', 'alice', 'reaction', 'message#alice'], + ['๐ฑ๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ฑ๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ฑ๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ฉ๐ฝโ๐ฆณ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝโ๐ฆณ', 'alice', 'reaction', 'message#alice'], + ['๐จ๐ฝโ๐ฆณ', 'alice', 'reaction', 'message#alice'], + ['๐ฉ๐ฝโ๐ฆฒ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝโ๐ฆฒ', 'alice', 'reaction', 'message#alice'], + ['๐จ๐ฝโ๐ฆฒ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ต๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ด๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ฒ๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ณ๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ณ๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ณ๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ฎ๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ฎ๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ฎ๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ท๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ท๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ท๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ต๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ต๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ต๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ฉ๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐จ๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ฉ๐ฝโ๐พ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝโ๐พ', 'alice', 'reaction', 'message#alice'], + ['๐จ๐ฝโ๐พ', 'alice', 'reaction', 'message#alice'], + ['๐ฉ๐ฝโ๐ณ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝโ๐ณ', 'alice', 'reaction', 'message#alice'], + ['๐จ๐ฝโ๐ณ', 'alice', 'reaction', 'message#alice'], + ['๐ฉ๐ฝโ๐', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝโ๐', 'alice', 'reaction', 'message#alice'], + ['๐จ๐ฝโ๐', 'alice', 'reaction', 'message#alice'], + ['๐ฉ๐ฝโ๐ค', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝโ๐ค', 'alice', 'reaction', 'message#alice'], + ['๐จ๐ฝโ๐ค', 'alice', 'reaction', 'message#alice'], + ['๐ฉ๐ฝโ๐ซ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝโ๐ซ', 'alice', 'reaction', 'message#alice'], + ['๐จ๐ฝโ๐ซ', 'alice', 'reaction', 'message#alice'], + ['๐ฉ๐ฝโ๐ญ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝโ๐ญ', 'alice', 'reaction', 'message#alice'], + ['๐จ๐ฝโ๐ญ', 'alice', 'reaction', 'message#alice'], + ['๐ฉ๐ฝโ๐ป', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝโ๐ป', 'alice', 'reaction', 'message#alice'], + ['๐จ๐ฝโ๐ป', 'alice', 'reaction', 'message#alice'], + ['๐ฉ๐ฝโ๐ผ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝโ๐ผ', 'alice', 'reaction', 'message#alice'], + ['๐จ๐ฝโ๐ผ', 'alice', 'reaction', 'message#alice'], + ['๐ฉ๐ฝโ๐ง', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝโ๐ง', 'alice', 'reaction', 'message#alice'], + ['๐จ๐ฝโ๐ง', 'alice', 'reaction', 'message#alice'], + ['๐ฉ๐ฝโ๐ฌ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝโ๐ฌ', 'alice', 'reaction', 'message#alice'], + ['๐จ๐ฝโ๐ฌ', 'alice', 'reaction', 'message#alice'], + ['๐ฉ๐ฝโ๐จ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝโ๐จ', 'alice', 'reaction', 'message#alice'], + ['๐จ๐ฝโ๐จ', 'alice', 'reaction', 'message#alice'], + ['๐ฉ๐ฝโ๐', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝโ๐', 'alice', 'reaction', 'message#alice'], + ['๐จ๐ฝโ๐', 'alice', 'reaction', 'message#alice'], + ['๐ฉ๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐จ๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ฉ๐ฝโ๐', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝโ๐', 'alice', 'reaction', 'message#alice'], + ['๐จ๐ฝโ๐', 'alice', 'reaction', 'message#alice'], + ['๐ฉ๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐จ๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ฐ๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ฐ๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ฐ๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐คต๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐คต๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐คต๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ธ๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ซ
๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐คด๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ฅท๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ฆธ๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ฆธ๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ฆธ๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ฆน๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ฆน๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ฆน๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐คถ๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝโ๐', 'alice', 'reaction', 'message#alice'], + ['๐
๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ผ๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐คฐ๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ซ๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ซ๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐คฑ๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ฉ๐ฝโ๐ผ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝโ๐ผ', 'alice', 'reaction', 'message#alice'], + ['๐จ๐ฝโ๐ผ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐
๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐
๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐
๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐คฆ๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐คฆ๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐คฆ๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐คท๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐คท๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐คท๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝโโ๏ธ', 'alice', 'reaction', 'message#alice'], + ['๐๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐บ๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ด๐ฝ', 'alice', 'reaction', 'message#alice'], + ['๐ฉ๐ฝโ๐ฆฝ', 'alice', 'reaction', 'message#alice'], + ['๐ง๐ฝโ๐ฆฝ', 'alice', 'reaction', 'message#alice'], + ], + [ + ['๐', 'alice'], + ['๐', 'alice'], + ['๐', 'alice'], + ['๐', 'alice'], + ['๐', 'alice'], + ['๐
', 'alice'], + ['๐', 'alice'], + ['๐คฃ', 'alice'], + ['๐ฅฒ', 'alice'], + ['๐ฅน', 'alice'], + ['โบ๏ธ', 'alice'], + ['๐', 'alice'], + ['๐', 'alice'], + ['๐', 'alice'], + ['๐', 'alice'], + ['๐', 'alice'], + ['๐', 'alice'], + ['๐', 'alice'], + ['๐ฅฐ', 'alice'], + ['๐', 'alice'], + ['๐', 'alice'], + ['๐', 'alice'], + ['๐', 'alice'], + ['๐', 'alice'], + ['๐', 'alice'], + ['๐', 'alice'], + ['๐', 'alice'], + ['๐คช', 'alice'], + ['๐คจ', 'alice'], + ['๐ง', 'alice'], + ['๐ค', 'alice'], + ['๐', 'alice'], + ['๐ฅธ', 'alice'], + ['๐คฉ', 'alice'], + ['๐ฅณ', 'alice'], + ['๐', 'alice'], + ['๐', 'alice'], + ['๐', 'alice'], + ['๐', 'alice'], + ['๐', 'alice'], + ['๐', 'alice'], + ['๐', 'alice'], + ['โน๏ธ', 'alice'], + ['๐ฃ', 'alice'], + ['๐', 'alice'], + ['๐ซ', 'alice'], + ['๐ฉ', 'alice'], + ['๐ฅบ', 'alice'], + ['๐ข', 'alice'], + ['๐ญ', 'alice'], + ['๐ฎโ๐จ', 'alice'], + ['๐ค', 'alice'], + ['๐ ', 'alice'], + ['๐ก', 'alice'], + ['๐คฌ', 'alice'], + ['๐คฏ', 'alice'], + ['๐ณ', 'alice'], + ['๐ฅต', 'alice'], + ['๐ฅถ', 'alice'], + ['๐ฑ', 'alice'], + ['๐จ', 'alice'], + ['๐ฐ', 'alice'], + ['๐ฅ', 'alice'], + ['๐', 'alice'], + ['๐ซฃ', 'alice'], + ['๐ค', 'alice'], + ['๐ซก', 'alice'], + ['๐ค', 'alice'], + ['๐ซข', 'alice'], + ['๐คญ', 'alice'], + ['๐คซ', 'alice'], + ['๐คฅ', 'alice'], + ['๐ถ', 'alice'], + ['๐ถโ๐ซ๏ธ', 'alice'], + ['๐', 'alice'], + ['๐', 'alice'], + ['๐ฌ', 'alice'], + ['๐ซ ', 'alice'], + ['๐', 'alice'], + ['๐ฏ', 'alice'], + ['๐ฆ', 'alice'], + ['๐ง', 'alice'], + ['๐ฎ', 'alice'], + ['๐ฒ', 'alice'], + ['๐ฅฑ', 'alice'], + ['๐ด', 'alice'], + ['๐คค', 'alice'], + ['๐ช', 'alice'], + ['๐ต', 'alice'], + ['๐ตโ๐ซ', 'alice'], + ['๐ซฅ', 'alice'], + ['๐ค', 'alice'], + ['๐ฅด', 'alice'], + ['๐คข', 'alice'], + ['๐คฎ', 'alice'], + ['๐คง', 'alice'], + ['๐ท', 'alice'], + ['๐ค', 'alice'], + ['๐ค', 'alice'], + ['๐ค', 'alice'], + ['๐ค ', 'alice'], + ['๐', 'alice'], + ['๐ฟ', 'alice'], + ['๐น', 'alice'], + ['๐บ', 'alice'], + ['๐คก', 'alice'], + ['๐ฉ', 'alice'], + ['๐ป', 'alice'], + ['๐', 'alice'], + ['โ ๏ธ', 'alice'], + ['๐ฝ', 'alice'], + ['๐พ', 'alice'], + ['๐ค', 'alice'], + ['๐', 'alice'], + ['๐บ', 'alice'], + ['๐ธ', 'alice'], + ['๐น', 'alice'], + ['๐ป', 'alice'], + ['๐ผ', 'alice'], + ['๐ฝ', 'alice'], + ['๐', 'alice'], + ['๐ฟ', 'alice'], + ['๐พ', 'alice'], + ['๐ถ', 'alice'], + ['๐ง', 'alice'], + ['๐ง', 'alice'], + ['๐ฆ', 'alice'], + ['๐ฉ', 'alice'], + ['๐ง', 'alice'], + ['๐จ', 'alice'], + ['๐ฉโ๐ฆฑ', 'alice'], + ['๐งโ๐ฆฑ', 'alice'], + ['๐จโ๐ฆฑ', 'alice'], + ['๐ฉโ๐ฆฐ', 'alice'], + ['๐งโ๐ฆฐ', 'alice'], + ['๐จโ๐ฆฐ', 'alice'], + ['๐ฑโโ๏ธ', 'alice'], + ['๐ฑ', 'alice'], + ['๐ฑโโ๏ธ', 'alice'], + ['๐ฉโ๐ฆณ', 'alice'], + ['๐งโ๐ฆณ', 'alice'], + ['๐จโ๐ฆณ', 'alice'], + ['๐ฉโ๐ฆฒ', 'alice'], + ['๐งโ๐ฆฒ', 'alice'], + ['๐จโ๐ฆฒ', 'alice'], + ['๐งโโ๏ธ', 'alice'], + ['๐ง', 'alice'], + ['๐งโโ๏ธ', 'alice'], + ['๐ต', 'alice'], + ['๐ง', 'alice'], + ['๐ด', 'alice'], + ['๐ฒ', 'alice'], + ['๐ณโโ๏ธ', 'alice'], + ['๐ณ', 'alice'], + ['๐ณโโ๏ธ', 'alice'], + ['๐ง', 'alice'], + ['๐ฎโโ๏ธ', 'alice'], + ['๐ฎ', 'alice'], + ['๐ฎโโ๏ธ', 'alice'], + ['๐ทโโ๏ธ', 'alice'], + ['๐ท', 'alice'], + ['๐ทโโ๏ธ', 'alice'], + ['๐โโ๏ธ', 'alice'], + ['๐', 'alice'], + ['๐โโ๏ธ', 'alice'], + ['๐ต๏ธโโ๏ธ', 'alice'], + ['๐ต๏ธ', 'alice'], + ['๐ต๏ธโโ๏ธ', 'alice'], + ['๐ฉโโ๏ธ', 'alice'], + ['๐งโโ๏ธ', 'alice'], + ['๐จโโ๏ธ', 'alice'], + ['๐ฉโ๐พ', 'alice'], + ['๐งโ๐พ', 'alice'], + ['๐จโ๐พ', 'alice'], + ['๐ฉโ๐ณ', 'alice'], + ['๐งโ๐ณ', 'alice'], + ['๐จโ๐ณ', 'alice'], + ['๐ฉโ๐', 'alice'], + ['๐งโ๐', 'alice'], + ['๐จโ๐', 'alice'], + ['๐ฉโ๐ค', 'alice'], + ['๐งโ๐ค', 'alice'], + ['๐จโ๐ค', 'alice'], + ['๐ฉโ๐ซ', 'alice'], + ['๐งโ๐ซ', 'alice'], + ['๐จโ๐ซ', 'alice'], + ['๐ฉโ๐ญ', 'alice'], + ['๐งโ๐ญ', 'alice'], + ['๐จโ๐ญ', 'alice'], + ['๐ฉโ๐ป', 'alice'], + ['๐งโ๐ป', 'alice'], + ['๐จโ๐ป', 'alice'], + ['๐ฉโ๐ผ', 'alice'], + ['๐งโ๐ผ', 'alice'], + ['๐จโ๐ผ', 'alice'], + ['๐ฉโ๐ง', 'alice'], + ['๐งโ๐ง', 'alice'], + ['๐จโ๐ง', 'alice'], + ['๐ฉโ๐ฌ', 'alice'], + ['๐งโ๐ฌ', 'alice'], + ['๐จโ๐ฌ', 'alice'], + ['๐ฉโ๐จ', 'alice'], + ['๐งโ๐จ', 'alice'], + ['๐จโ๐จ', 'alice'], + ['๐ฉโ๐', 'alice'], + ['๐งโ๐', 'alice'], + ['๐จโ๐', 'alice'], + ['๐ฉโโ๏ธ', 'alice'], + ['๐งโโ๏ธ', 'alice'], + ['๐จโโ๏ธ', 'alice'], + ['๐ฉโ๐', 'alice'], + ['๐งโ๐', 'alice'], + ['๐จโ๐', 'alice'], + ['๐ฉโโ๏ธ', 'alice'], + ['๐งโโ๏ธ', 'alice'], + ['๐จโโ๏ธ', 'alice'], + ['๐ฐโโ๏ธ', 'alice'], + ['๐ฐ', 'alice'], + ['๐ฐโโ๏ธ', 'alice'], + ['๐คตโโ๏ธ', 'alice'], + ['๐คต', 'alice'], + ['๐คตโโ๏ธ', 'alice'], + ['๐ธ', 'alice'], + ['๐ซ
', 'alice'], + ['๐คด', 'alice'], + ['๐ฅท', 'alice'], + ['๐ฆธโโ๏ธ', 'alice'], + ['๐ฆธ', 'alice'], + ['๐ฆธโโ๏ธ', 'alice'], + ['๐ฆนโโ๏ธ', 'alice'], + ['๐ฆน', 'alice'], + ['๐ฆนโโ๏ธ', 'alice'], + ['๐คถ', 'alice'], + ['๐งโ๐', 'alice'], + ['๐
', 'alice'], + ['๐งโโ๏ธ', 'alice'], + ['๐ง', 'alice'], + ['๐งโโ๏ธ', 'alice'], + ['๐งโโ๏ธ', 'alice'], + ['๐ง', 'alice'], + ['๐งโโ๏ธ', 'alice'], + ['๐งโโ๏ธ', 'alice'], + ['๐ง', 'alice'], + ['๐งโโ๏ธ', 'alice'], + ['๐งโโ๏ธ', 'alice'], + ['๐ง', 'alice'], + ['๐งโโ๏ธ', 'alice'], + ['๐งโโ๏ธ', 'alice'], + ['๐ง', 'alice'], + ['๐งโโ๏ธ', 'alice'], + ['๐งโโ๏ธ', 'alice'], + ['๐ง', 'alice'], + ['๐งโโ๏ธ', 'alice'], + ['๐งโโ๏ธ', 'alice'], + ['๐ง', 'alice'], + ['๐งโโ๏ธ', 'alice'], + ['๐ง', 'alice'], + ['๐ผ', 'alice'], + ['๐คฐ', 'alice'], + ['๐ซ', 'alice'], + ['๐ซ', 'alice'], + ['๐คฑ', 'alice'], + ['๐ฉโ๐ผ', 'alice'], + ['๐งโ๐ผ', 'alice'], + ['๐จโ๐ผ', 'alice'], + ['๐โโ๏ธ', 'alice'], + ['๐', 'alice'], + ['๐โโ๏ธ', 'alice'], + ['๐โโ๏ธ', 'alice'], + ['๐', 'alice'], + ['๐โโ๏ธ', 'alice'], + ['๐
โโ๏ธ', 'alice'], + ['๐
', 'alice'], + ['๐
โโ๏ธ', 'alice'], + ['๐โโ๏ธ', 'alice'], + ['๐', 'alice'], + ['๐โโ๏ธ', 'alice'], + ['๐โโ๏ธ', 'alice'], + ['๐', 'alice'], + ['๐โโ๏ธ', 'alice'], + ['๐งโโ๏ธ', 'alice'], + ['๐ง', 'alice'], + ['๐งโโ๏ธ', 'alice'], + ['๐คฆโโ๏ธ', 'alice'], + ['๐คฆ', 'alice'], + ['๐คฆโโ๏ธ', 'alice'], + ['๐คทโโ๏ธ', 'alice'], + ['๐คท', 'alice'], + ['๐คทโโ๏ธ', 'alice'], + ['๐โโ๏ธ', 'alice'], + ['๐', 'alice'], + ['๐โโ๏ธ', 'alice'], + ['๐โโ๏ธ', 'alice'], + ['๐', 'alice'], + ['๐โโ๏ธ', 'alice'], + ['๐โโ๏ธ', 'alice'], + ['๐', 'alice'], + ['๐โโ๏ธ', 'alice'], + ['๐โโ๏ธ', 'alice'], + ['๐', 'alice'], + ['๐โโ๏ธ', 'alice'], + ['๐งโโ๏ธ', 'alice'], + ['๐ง', 'alice'], + ['๐งโโ๏ธ', 'alice'], + ['๐
', 'alice'], + ['๐คณ', 'alice'], + ['๐', 'alice'], + ['๐บ', 'alice'], + ['๐ฏโโ๏ธ', 'alice'], + ['๐ฏ', 'alice'], + ['๐ฏโโ๏ธ', 'alice'], + ['๐ด', 'alice'], + ['๐ฉโ๐ฆฝ', 'alice'], + ['๐งโ๐ฆฝ', 'alice'], + ['๐จโ๐ฆฝ', 'alice'], + ['๐ฉโ๐ฆผ', 'alice'], + ['๐งโ๐ฆผ', 'alice'], + ['๐จโ๐ฆผ', 'alice'], + ['๐ถโโ๏ธ', 'alice'], + ['๐ถ', 'alice'], + ['๐ถโโ๏ธ', 'alice'], + ['๐ฉโ๐ฆฏ', 'alice'], + ['๐งโ๐ฆฏ', 'alice'], + ['๐จโ๐ฆฏ', 'alice'], + ['๐งโโ๏ธ', 'alice'], + ['๐ง', 'alice'], + ['๐งโโ๏ธ', 'alice'], + ['๐โโ๏ธ', 'alice'], + ['๐', 'alice'], + ['๐โโ๏ธ', 'alice'], + ['๐งโโ๏ธ', 'alice'], + ['๐ง', 'alice'], + ['๐งโโ๏ธ', 'alice'], + ['๐ญ', 'alice'], + ['๐งโ๐คโ๐ง', 'alice'], + ['๐ฌ', 'alice'], + ['๐ซ', 'alice'], + ['๐ฉโโค๏ธโ๐ฉ', 'alice'], + ['๐', 'alice'], + ['๐จโโค๏ธโ๐จ', 'alice'], + ['๐ฉโโค๏ธโ๐จ', 'alice'], + ['๐ฉโโค๏ธโ๐โ๐ฉ', 'alice'], + ['๐', 'alice'], + ['๐จโโค๏ธโ๐โ๐จ', 'alice'], + ['๐ฉโโค๏ธโ๐โ๐จ', 'alice'], + ['๐ช', 'alice'], + ['๐จโ๐ฉโ๐ฆ', 'alice'], + ['๐จโ๐ฉโ๐ง', 'alice'], + ['๐จโ๐ฉโ๐งโ๐ฆ', 'alice'], + ['๐จโ๐ฉโ๐ฆโ๐ฆ', 'alice'], + ['๐จโ๐ฉโ๐งโ๐ง', 'alice'], + ['๐จโ๐จโ๐ฆ', 'alice'], + ['๐จโ๐จโ๐ง', 'alice'], + ['๐จโ๐จโ๐งโ๐ฆ', 'alice'], + ['๐จโ๐จโ๐ฆโ๐ฆ', 'alice'], + ['๐จโ๐จโ๐งโ๐ง', 'alice'], + ['๐ฉโ๐ฉโ๐ฆ', 'alice'], + ['๐ฉโ๐ฉโ๐ง', 'alice'], + ['๐ฉโ๐ฉโ๐งโ๐ฆ', 'alice'], + ['๐ฉโ๐ฉโ๐ฆโ๐ฆ', 'alice'], + ['๐ฉโ๐ฉโ๐งโ๐ง', 'alice'], + ['๐จโ๐ฆ', 'alice'], + ['๐จโ๐ฆโ๐ฆ', 'alice'], + ['๐จโ๐ง', 'alice'], + ['๐จโ๐งโ๐ฆ', 'alice'], + ['๐จโ๐งโ๐ง', 'alice'], + ['๐ฉโ๐ฆ', 'alice'], + ['๐ฉโ๐ฆโ๐ฆ', 'alice'], + ['๐ฉโ๐ง', 'alice'], + ['๐ฉโ๐งโ๐ฆ', 'alice'], + ['๐ฉโ๐งโ๐ง', 'alice'], + ['๐ฃ', 'alice'], + ['๐ค', 'alice'], + ['๐ฅ', 'alice'], + ['๐ซ', 'alice'], + ['๐๐ฝ', 'alice'], + ['๐ค๐ฝ', 'alice'], + ['๐๐ฝ', 'alice'], + ['โ๐ฝ', 'alice'], + ['๐๐ฝ', 'alice'], + ['๐๐ฝ', 'alice'], + ['๐ค๐ฝ', 'alice'], + ['๐ค๐ฝ', 'alice'], + ['โ๐ฝ', 'alice'], + ['๐ค๐ฝ', 'alice'], + ['๐ซฐ๐ฝ', 'alice'], + ['๐ค๐ฝ', 'alice'], + ['๐ค๐ฝ', 'alice'], + ['๐ค๐ฝ', 'alice'], + ['๐ซต๐ฝ', 'alice'], + ['๐ซฑ๐ฝ', 'alice'], + ['๐ซฒ๐ฝ', 'alice'], + ['๐ซณ๐ฝ', 'alice'], + ['๐ซด๐ฝ', 'alice'], + ['๐๐ฝ', 'alice'], + ['๐๐ฝ', 'alice'], + ['๐๐ฝ', 'alice'], + ['๐๐ฝ', 'alice'], + ['๐๐ฝ', 'alice'], + ['โ๐ฝ', 'alice'], + ['๐๐ฝ', 'alice'], + ['๐๐ฝ', 'alice'], + ['โ๐ฝ', 'alice'], + ['๐๐ฝ', 'alice'], + ['๐ค๐ฝ', 'alice'], + ['๐ค๐ฝ', 'alice'], + ['๐๐ฝ', 'alice'], + ['๐ซถ๐ฝ', 'alice'], + ['๐๐ฝ', 'alice'], + ['๐๐ฝ', 'alice'], + ['๐คฒ๐ฝ', 'alice'], + ['๐๐ฝ', 'alice'], + ['โ๐ฝ', 'alice'], + ['๐
๐ฝ', 'alice'], + ['๐คณ๐ฝ', 'alice'], + ['๐ช๐ฝ', 'alice'], + ['๐ฆต๐ฝ', 'alice'], + ['๐ฆถ๐ฝ', 'alice'], + ['๐๐ฝ', 'alice'], + ['๐ฆป๐ฝ', 'alice'], + ['๐๐ฝ', 'alice'], + ['๐ถ๐ฝ', 'alice'], + ['๐ง๐ฝ', 'alice'], + ['๐ง๐ฝ', 'alice'], + ['๐ฆ๐ฝ', 'alice'], + ['๐ฉ๐ฝ', 'alice'], + ['๐ง๐ฝ', 'alice'], + ['๐จ๐ฝ', 'alice'], + ['๐ฉ๐ฝโ๐ฆฑ', 'alice'], + ['๐ง๐ฝโ๐ฆฑ', 'alice'], + ['๐จ๐ฝโ๐ฆฑ', 'alice'], + ['๐ฉ๐ฝโ๐ฆฐ', 'alice'], + ['๐ง๐ฝโ๐ฆฐ', 'alice'], + ['๐จ๐ฝโ๐ฆฐ', 'alice'], + ['๐ฑ๐ฝโโ๏ธ', 'alice'], + ['๐ฑ๐ฝ', 'alice'], + ['๐ฑ๐ฝโโ๏ธ', 'alice'], + ['๐ฉ๐ฝโ๐ฆณ', 'alice'], + ['๐ง๐ฝโ๐ฆณ', 'alice'], + ['๐จ๐ฝโ๐ฆณ', 'alice'], + ['๐ฉ๐ฝโ๐ฆฒ', 'alice'], + ['๐ง๐ฝโ๐ฆฒ', 'alice'], + ['๐จ๐ฝโ๐ฆฒ', 'alice'], + ['๐ง๐ฝโโ๏ธ', 'alice'], + ['๐ง๐ฝ', 'alice'], + ['๐ง๐ฝโโ๏ธ', 'alice'], + ['๐ต๐ฝ', 'alice'], + ['๐ง๐ฝ', 'alice'], + ['๐ด๐ฝ', 'alice'], + ['๐ฒ๐ฝ', 'alice'], + ['๐ณ๐ฝโโ๏ธ', 'alice'], + ['๐ณ๐ฝ', 'alice'], + ['๐ณ๐ฝโโ๏ธ', 'alice'], + ['๐ง๐ฝ', 'alice'], + ['๐ฎ๐ฝโโ๏ธ', 'alice'], + ['๐ฎ๐ฝ', 'alice'], + ['๐ฎ๐ฝโโ๏ธ', 'alice'], + ['๐ท๐ฝโโ๏ธ', 'alice'], + ['๐ท๐ฝ', 'alice'], + ['๐ท๐ฝโโ๏ธ', 'alice'], + ['๐๐ฝโโ๏ธ', 'alice'], + ['๐๐ฝ', 'alice'], + ['๐๐ฝโโ๏ธ', 'alice'], + ['๐ต๐ฝโโ๏ธ', 'alice'], + ['๐ต๐ฝ', 'alice'], + ['๐ต๐ฝโโ๏ธ', 'alice'], + ['๐ฉ๐ฝโโ๏ธ', 'alice'], + ['๐ง๐ฝโโ๏ธ', 'alice'], + ['๐จ๐ฝโโ๏ธ', 'alice'], + ['๐ฉ๐ฝโ๐พ', 'alice'], + ['๐ง๐ฝโ๐พ', 'alice'], + ['๐จ๐ฝโ๐พ', 'alice'], + ['๐ฉ๐ฝโ๐ณ', 'alice'], + ['๐ง๐ฝโ๐ณ', 'alice'], + ['๐จ๐ฝโ๐ณ', 'alice'], + ['๐ฉ๐ฝโ๐', 'alice'], + ['๐ง๐ฝโ๐', 'alice'], + ['๐จ๐ฝโ๐', 'alice'], + ['๐ฉ๐ฝโ๐ค', 'alice'], + ['๐ง๐ฝโ๐ค', 'alice'], + ['๐จ๐ฝโ๐ค', 'alice'], + ['๐ฉ๐ฝโ๐ซ', 'alice'], + ['๐ง๐ฝโ๐ซ', 'alice'], + ['๐จ๐ฝโ๐ซ', 'alice'], + ['๐ฉ๐ฝโ๐ญ', 'alice'], + ['๐ง๐ฝโ๐ญ', 'alice'], + ['๐จ๐ฝโ๐ญ', 'alice'], + ['๐ฉ๐ฝโ๐ป', 'alice'], + ['๐ง๐ฝโ๐ป', 'alice'], + ['๐จ๐ฝโ๐ป', 'alice'], + ['๐ฉ๐ฝโ๐ผ', 'alice'], + ['๐ง๐ฝโ๐ผ', 'alice'], + ['๐จ๐ฝโ๐ผ', 'alice'], + ['๐ฉ๐ฝโ๐ง', 'alice'], + ['๐ง๐ฝโ๐ง', 'alice'], + ['๐จ๐ฝโ๐ง', 'alice'], + ['๐ฉ๐ฝโ๐ฌ', 'alice'], + ['๐ง๐ฝโ๐ฌ', 'alice'], + ['๐จ๐ฝโ๐ฌ', 'alice'], + ['๐ฉ๐ฝโ๐จ', 'alice'], + ['๐ง๐ฝโ๐จ', 'alice'], + ['๐จ๐ฝโ๐จ', 'alice'], + ['๐ฉ๐ฝโ๐', 'alice'], + ['๐ง๐ฝโ๐', 'alice'], + ['๐จ๐ฝโ๐', 'alice'], + ['๐ฉ๐ฝโโ๏ธ', 'alice'], + ['๐ง๐ฝโโ๏ธ', 'alice'], + ['๐จ๐ฝโโ๏ธ', 'alice'], + ['๐ฉ๐ฝโ๐', 'alice'], + ['๐ง๐ฝโ๐', 'alice'], + ['๐จ๐ฝโ๐', 'alice'], + ['๐ฉ๐ฝโโ๏ธ', 'alice'], + ['๐ง๐ฝโโ๏ธ', 'alice'], + ['๐จ๐ฝโโ๏ธ', 'alice'], + ['๐ฐ๐ฝโโ๏ธ', 'alice'], + ['๐ฐ๐ฝ', 'alice'], + ['๐ฐ๐ฝโโ๏ธ', 'alice'], + ['๐คต๐ฝโโ๏ธ', 'alice'], + ['๐คต๐ฝ', 'alice'], + ['๐คต๐ฝโโ๏ธ', 'alice'], + ['๐ธ๐ฝ', 'alice'], + ['๐ซ
๐ฝ', 'alice'], + ['๐คด๐ฝ', 'alice'], + ['๐ฅท๐ฝ', 'alice'], + ['๐ฆธ๐ฝโโ๏ธ', 'alice'], + ['๐ฆธ๐ฝ', 'alice'], + ['๐ฆธ๐ฝโโ๏ธ', 'alice'], + ['๐ฆน๐ฝโโ๏ธ', 'alice'], + ['๐ฆน๐ฝ', 'alice'], + ['๐ฆน๐ฝโโ๏ธ', 'alice'], + ['๐คถ๐ฝ', 'alice'], + ['๐ง๐ฝโ๐', 'alice'], + ['๐
๐ฝ', 'alice'], + ['๐ง๐ฝโโ๏ธ', 'alice'], + ['๐ง๐ฝ', 'alice'], + ['๐ง๐ฝโโ๏ธ', 'alice'], + ['๐ง๐ฝโโ๏ธ', 'alice'], + ['๐ง๐ฝ', 'alice'], + ['๐ง๐ฝโโ๏ธ', 'alice'], + ['๐ง๐ฝโโ๏ธ', 'alice'], + ['๐ง๐ฝ', 'alice'], + ['๐ง๐ฝโโ๏ธ', 'alice'], + ['๐ง๐ฝโโ๏ธ', 'alice'], + ['๐ง๐ฝ', 'alice'], + ['๐ง๐ฝโโ๏ธ', 'alice'], + ['๐ง๐ฝโโ๏ธ', 'alice'], + ['๐ง๐ฝ', 'alice'], + ['๐ง๐ฝโโ๏ธ', 'alice'], + ['๐ผ๐ฝ', 'alice'], + ['๐คฐ๐ฝ', 'alice'], + ['๐ซ๐ฝ', 'alice'], + ['๐ซ๐ฝ', 'alice'], + ['๐คฑ๐ฝ', 'alice'], + ['๐ฉ๐ฝโ๐ผ', 'alice'], + ['๐ง๐ฝโ๐ผ', 'alice'], + ['๐จ๐ฝโ๐ผ', 'alice'], + ['๐๐ฝโโ๏ธ', 'alice'], + ['๐๐ฝ', 'alice'], + ['๐๐ฝโโ๏ธ', 'alice'], + ['๐๐ฝโโ๏ธ', 'alice'], + ['๐๐ฝ', 'alice'], + ['๐๐ฝโโ๏ธ', 'alice'], + ['๐
๐ฝโโ๏ธ', 'alice'], + ['๐
๐ฝ', 'alice'], + ['๐
๐ฝโโ๏ธ', 'alice'], + ['๐๐ฝโโ๏ธ', 'alice'], + ['๐๐ฝ', 'alice'], + ['๐๐ฝโโ๏ธ', 'alice'], + ['๐๐ฝโโ๏ธ', 'alice'], + ['๐๐ฝ', 'alice'], + ['๐๐ฝโโ๏ธ', 'alice'], + ['๐ง๐ฝโโ๏ธ', 'alice'], + ['๐ง๐ฝ', 'alice'], + ['๐ง๐ฝโโ๏ธ', 'alice'], + ['๐คฆ๐ฝโโ๏ธ', 'alice'], + ['๐คฆ๐ฝ', 'alice'], + ['๐คฆ๐ฝโโ๏ธ', 'alice'], + ['๐คท๐ฝโโ๏ธ', 'alice'], + ['๐คท๐ฝ', 'alice'], + ['๐คท๐ฝโโ๏ธ', 'alice'], + ['๐๐ฝโโ๏ธ', 'alice'], + ['๐๐ฝ', 'alice'], + ['๐๐ฝโโ๏ธ', 'alice'], + ['๐๐ฝโโ๏ธ', 'alice'], + ['๐๐ฝ', 'alice'], + ['๐๐ฝโโ๏ธ', 'alice'], + ['๐๐ฝโโ๏ธ', 'alice'], + ['๐๐ฝ', 'alice'], + ['๐๐ฝโโ๏ธ', 'alice'], + ['๐๐ฝโโ๏ธ', 'alice'], + ['๐๐ฝ', 'alice'], + ['๐๐ฝโโ๏ธ', 'alice'], + ['๐ง๐ฝโโ๏ธ', 'alice'], + ['๐ง๐ฝ', 'alice'], + ['๐ง๐ฝโโ๏ธ', 'alice'], + ['๐๐ฝ', 'alice'], + ['๐บ๐ฝ', 'alice'], + ['๐ด๐ฝ', 'alice'], + ['๐ฉ๐ฝโ๐ฆฝ', 'alice'], + ['๐ง๐ฝโ๐ฆฝ', 'alice'], + ], + ], + ]; + } + + #[\PHPUnit\Framework\Attributes\DataProvider('providerTestRetrieveAllReactionsWithSpecificReaction')] + public function testRetrieveAllReactionsWithSpecificReaction(array $comments, string $reaction, array $expected): void { + $this->skipIfNotSupport4ByteUTF(); + $manager = $this->getManager(); + + $processedComments = $this->proccessComments($comments); + $comment = reset($processedComments); + $all = $manager->retrieveAllReactionsWithSpecificReaction((int)$comment->getId(), $reaction); + $actual = array_map(static function (IComment $row): array { + return [ + $row->getActorId(), + $row->getMessage(), + ]; + }, $all); + $this->assertEqualsCanonicalizing($expected, $actual); + } + + public static function providerTestRetrieveAllReactionsWithSpecificReaction(): array { + return [ + [ + [ + ['message', 'alice', 'comment', null], + ], + '๐', + [], + ], + [ + [ + ['message', 'alice', 'comment', null], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐', 'frank', 'reaction', 'message#alice'], + ], + '๐', + [ + ['๐', 'alice'], + ['๐', 'frank'], + ], + ], + [ + [ + ['message', 'alice', 'comment', null], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐', 'alice', 'reaction', 'message#alice'], + ['๐', 'frank', 'reaction', 'message#alice'], + ], + '๐', + [ + ['๐', 'alice'], + ], + ], + ]; + } + + #[\PHPUnit\Framework\Attributes\DataProvider('providerTestGetReactionComment')] + public function testGetReactionComment(array $comments, array $expected, bool $notFound): void { + $this->skipIfNotSupport4ByteUTF(); + $manager = $this->getManager(); + + $processedComments = $this->proccessComments($comments); + + $keys = ['message', 'actorId', 'verb', 'parent']; + $expected = array_combine($keys, $expected); + + if ($notFound) { + $this->expectException(NotFoundException::class); + } + $comment = $processedComments[$expected['message'] . '#' . $expected['actorId']]; + $actual = $manager->getReactionComment((int)$comment->getParentId(), $comment->getActorType(), $comment->getActorId(), $comment->getMessage()); + if (!$notFound) { + $this->assertEquals($expected['message'], $actual->getMessage()); + $this->assertEquals($expected['actorId'], $actual->getActorId()); + $this->assertEquals($expected['verb'], $actual->getVerb()); + $this->assertEquals($processedComments[$expected['parent']]->getId(), $actual->getParentId()); + } + } + + public static function providerTestGetReactionComment(): array { + return [ + [ + [ + ['message', 'Matthew', 'comment', null], + ['๐', 'Matthew', 'reaction', 'message#Matthew'], + ['๐', 'Mark', 'reaction', 'message#Matthew'], + ['๐', 'Luke', 'reaction', 'message#Matthew'], + ['๐', 'John', 'reaction', 'message#Matthew'], + ], + ['๐', 'Matthew', 'reaction', 'message#Matthew'], + false, + ], + [ + [ + ['message', 'Matthew', 'comment', null], + ['๐', 'Matthew', 'reaction', 'message#Matthew'], + ['๐', 'Mark', 'reaction', 'message#Matthew'], + ['๐', 'Luke', 'reaction', 'message#Matthew'], + ['๐', 'John', 'reaction', 'message#Matthew'], + ], + ['๐', 'Mark', 'reaction', 'message#Matthew'], + false, + ], + [ + [ + ['message', 'Matthew', 'comment', null], + ['๐', 'Matthew', 'reaction', 'message#Matthew'], + ], + ['๐', 'Matthew', 'reaction', 'message#Matthew'], + false, + ], + [ + [ + ['message', 'Matthew', 'comment', null], + ['๐', 'Matthew', 'reaction', 'message#Matthew'], + ['๐', 'Matthew', 'reaction_deleted', 'message#Matthew'], + ], + ['๐', 'Matthew', 'reaction', 'message#Matthew'], + true, + ], + ]; + } + + #[\PHPUnit\Framework\Attributes\DataProvider('providerTestReactionMessageSize')] + public function testReactionMessageSize(string $reactionString, bool $valid): void { + $this->skipIfNotSupport4ByteUTF(); + if (!$valid) { + $this->expectException(\UnexpectedValueException::class); + } + + $manager = $this->getManager(); + $comment = new Comment(); + $comment->setMessage($reactionString) + ->setVerb('reaction') + ->setActor('users', 'alice') + ->setObject('files', 'file64'); + $status = $manager->save($comment); + $this->assertTrue($status); + } + + public static function providerTestReactionMessageSize(): array { + return [ + ['a', false], + ['1', false], + ['๐', true], + ['๐๐', false], + ['๐๐ฝ', true], + ['๐จ๐ฝโ๐ป', true], + ['๐จ๐ฝโ๐ป๐', false], + ]; + } + + #[\PHPUnit\Framework\Attributes\DataProvider('providerTestReactionsSummarizeOrdered')] + public function testReactionsSummarizeOrdered(array $comments, array $expected, bool $isFullMatch): void { + $this->skipIfNotSupport4ByteUTF(); + $manager = $this->getManager(); + + + $processedComments = $this->proccessComments($comments); + $comment = end($processedComments); + $actual = $manager->get($comment->getParentId()); + + if ($isFullMatch) { + $this->assertSame($expected, $actual->getReactions()); + } else { + $subResult = array_slice($actual->getReactions(), 0, count($expected)); + $this->assertSame($expected, $subResult); + } + } + + public static function providerTestReactionsSummarizeOrdered(): array { + return [ + [ + [ + ['message', 'alice', 'comment', null], + ['๐', 'alice', 'reaction', 'message#alice'], + ], + ['๐' => 1], + true, + ], + [ + [ + ['message', 'alice', 'comment', null], + ['๐', 'John', 'reaction', 'message#alice'], + ['๐ผ', 'Luke', 'reaction', 'message#alice'], + ['๐', 'Luke', 'reaction', 'message#alice'], + ['๐', 'Luke', 'reaction', 'message#alice'], + ['๐ค', 'Luke', 'reaction', 'message#alice'], + ['๐', 'Luke', 'reaction', 'message#alice'], + ['๐', 'Luke', 'reaction', 'message#alice'], + ['๐', 'Luke', 'reaction', 'message#alice'], + ['๐ฅ', 'Luke', 'reaction', 'message#alice'], + ['๐', 'Luke', 'reaction', 'message#alice'], + ['โ', 'Luke', 'reaction', 'message#alice'], + ['๐', 'Luke', 'reaction', 'message#alice'], + ['๐', 'Luke', 'reaction', 'message#alice'], + ['๐', 'Luke', 'reaction', 'message#alice'], + ['๐', 'Luke', 'reaction', 'message#alice'], + ['๐ธ', 'Luke', 'reaction', 'message#alice'], + ['๐ฐ', 'Luke', 'reaction', 'message#alice'], + ['โ๏ธ', 'Luke', 'reaction', 'message#alice'], + ['๐จ', 'Luke', 'reaction', 'message#alice'], + ['๐ฅ', 'Luke', 'reaction', 'message#alice'], + ['๐', 'Paul', 'reaction', 'message#alice'], + ['๐', 'Peter', 'reaction', 'message#alice'], + ['๐', 'Matthew', 'reaction', 'message#alice'], + ['๐', 'Mark', 'reaction', 'message#alice'], + ['๐', 'Luke', 'reaction', 'message#alice'], + ], + [ + '๐' => 3, + '๐' => 2, + ], + false, + ], + ]; + } +} |