feat: implement global autoresponder plugin
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
final class RuleRepository
|
||||
{
|
||||
public function __construct(private string $baseDir = Settings::DATA_DIR)
|
||||
{
|
||||
}
|
||||
|
||||
/** @return array<int,array<string,mixed>> */
|
||||
public function all(string $username): array
|
||||
{
|
||||
$data = $this->read($username);
|
||||
return array_values($data['rules'] ?? []);
|
||||
}
|
||||
|
||||
/** @return array<string,mixed>|null */
|
||||
public function find(string $username, string $id): ?array
|
||||
{
|
||||
foreach ($this->all($username) as $rule) {
|
||||
if (($rule['id'] ?? '') === $id) {
|
||||
return $rule;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @param array<string,mixed> $rule @return array<string,mixed> */
|
||||
public function create(string $username, array $rule): array
|
||||
{
|
||||
$data = $this->read($username);
|
||||
$now = time();
|
||||
$rule['id'] = bin2hex(random_bytes(12));
|
||||
$rule['created_at'] = $now;
|
||||
$rule['updated_at'] = $now;
|
||||
$data['rules'][] = $rule;
|
||||
$this->write($username, $data);
|
||||
return $rule;
|
||||
}
|
||||
|
||||
/** @param array<string,mixed> $patch @return array<string,mixed> */
|
||||
public function update(string $username, string $id, array $patch): array
|
||||
{
|
||||
$data = $this->read($username);
|
||||
foreach ($data['rules'] as $i => $rule) {
|
||||
if (($rule['id'] ?? '') === $id) {
|
||||
$updated = array_merge($rule, $patch, ['id' => $id, 'updated_at' => time()]);
|
||||
$data['rules'][$i] = $updated;
|
||||
$this->write($username, $data);
|
||||
return $updated;
|
||||
}
|
||||
}
|
||||
throw new RuntimeException('Rule not found');
|
||||
}
|
||||
|
||||
public function delete(string $username, string $id): void
|
||||
{
|
||||
$data = $this->read($username);
|
||||
$before = count($data['rules']);
|
||||
$data['rules'] = array_values(array_filter($data['rules'], static fn (array $rule): bool => ($rule['id'] ?? '') !== $id));
|
||||
if (count($data['rules']) === $before) {
|
||||
throw new RuntimeException('Rule not found');
|
||||
}
|
||||
$this->write($username, $data);
|
||||
}
|
||||
|
||||
/** @return array{rules:array<int,array<string,mixed>>} */
|
||||
private function read(string $username): array
|
||||
{
|
||||
$path = $this->path($username);
|
||||
if (!is_file($path)) {
|
||||
return ['rules' => []];
|
||||
}
|
||||
$data = json_decode((string)file_get_contents($path), true);
|
||||
return is_array($data) && isset($data['rules']) && is_array($data['rules']) ? $data : ['rules' => []];
|
||||
}
|
||||
|
||||
/** @param array{rules:array<int,array<string,mixed>>} $data */
|
||||
private function write(string $username, array $data): void
|
||||
{
|
||||
$path = $this->path($username);
|
||||
$dir = dirname($path);
|
||||
if (!is_dir($dir)) {
|
||||
mkdir($dir, 0700, true);
|
||||
}
|
||||
$tmp = $path . '.tmp.' . getmypid();
|
||||
file_put_contents($tmp, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), LOCK_EX);
|
||||
@chmod($tmp, 0600);
|
||||
rename($tmp, $path);
|
||||
}
|
||||
|
||||
private function path(string $username): string
|
||||
{
|
||||
$this->assertUsername($username);
|
||||
return rtrim($this->baseDir, '/') . '/users/' . $username . '/rules.json';
|
||||
}
|
||||
|
||||
private function assertUsername(string $username): void
|
||||
{
|
||||
if (!preg_match('/^[A-Za-z0-9._-]+$/', $username)) {
|
||||
throw new InvalidArgumentException('Invalid username');
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user