*/ class PendingToolConfirmationMapper extends QBMapper { public function __construct(IDBConnection $db) { parent::__construct($db, 'educai_tool_confirmations', PendingToolConfirmation::class); } /** @throws DoesNotExistException */ public function findActiveForAction(string $userId, ?int $botId, ?string $roomToken, string $toolName, string $argumentsHash, ?int $confirmationMessageId, int $now): PendingToolConfirmation { if ($confirmationMessageId === null) { throw new DoesNotExistException('A later Talk message is required to confirm this action.'); } $qb = $this->db->getQueryBuilder(); $qb->select('*')->from($this->getTableName()) ->where($qb->expr()->eq('user_id', $qb->createNamedParameter($userId))) ->andWhere($qb->expr()->eq('tool_name', $qb->createNamedParameter($toolName))) ->andWhere($qb->expr()->eq('arguments_hash', $qb->createNamedParameter($argumentsHash))) ->andWhere($qb->expr()->lt('talk_message_id', $qb->createNamedParameter($confirmationMessageId, IQueryBuilder::PARAM_INT))) ->andWhere($qb->expr()->gt('expires_at', $qb->createNamedParameter($now, IQueryBuilder::PARAM_INT))) ->orderBy('created_at', 'DESC') ->setMaxResults(1); $this->addNullableMatch($qb, 'bot_id', $botId); $this->addNullableMatch($qb, 'room_token', $roomToken); return $this->findEntity($qb); } public function claim(PendingToolConfirmation $confirmation, int $now): bool { $qb = $this->db->getQueryBuilder(); $qb->delete($this->getTableName()) ->where($qb->expr()->eq('id', $qb->createNamedParameter($confirmation->getId(), IQueryBuilder::PARAM_INT))) ->andWhere($qb->expr()->gt('expires_at', $qb->createNamedParameter($now, IQueryBuilder::PARAM_INT))); return $qb->executeStatement() === 1; } public function cleanupExpired(int $now): int { $qb = $this->db->getQueryBuilder(); $qb->delete($this->getTableName()) ->where($qb->expr()->lte('expires_at', $qb->createNamedParameter($now, IQueryBuilder::PARAM_INT))); return $qb->executeStatement(); } private function addNullableMatch(IQueryBuilder $qb, string $column, int|string|null $value): void { if ($value === null) { $qb->andWhere($qb->expr()->isNull($column)); return; } $parameterType = is_int($value) ? IQueryBuilder::PARAM_INT : IQueryBuilder::PARAM_STR; $qb->andWhere($qb->expr()->eq($column, $qb->createNamedParameter($value, $parameterType))); } }