Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions src/Rest/Helpers/Channel/Channel/ThreadChannelBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?php

declare(strict_types=1);

namespace Tempcord\Discord\Rest\Helpers\Channel\Channel;

use Tempcord\Discord\Rest\Helpers\Channel\Channel\Shared\SetRateLimitPerUser;

/**
* Modifies a thread, including a post in a forum or media channel.
*
* A thread is not an ordinary channel: it has no topic, no permission
* overwrites and no parent to move it between, and it has archiving, locking
* and — in a forum — tags, which no other channel has. Passing a thread to one
* of the guild channel builders sends fields Discord rejects for it and offers
* none of the ones that actually apply.
*
* @see https://discord.com/developers/docs/resources/channel#modify-channel-json-params-thread
*/
class ThreadChannelBuilder extends ChannelBuilder
{
use SetRateLimitPerUser;

/**
* The forum tags on this post, given as ids.
*
* The whole set is replaced, because that is what Discord does with the
* field: a tag left out of the list is a tag taken off the post. At most
* five, and a tag marked as moderated can only be applied by someone with
* Manage Threads.
*
* @param list<string> $tagIds
*/
public function setAppliedTags(array $tagIds): self
{
$this->data['applied_tags'] = array_values($tagIds);

return $this;
}

/**
* @return list<string>|null
*/
public function getAppliedTags(): ?array
{
return $this->data['applied_tags'] ?? null;
}

public function setArchived(bool $archived): self
{
$this->data['archived'] = $archived;

return $this;
}

/**
* A locked thread can still be read, but only a moderator may unarchive it.
*/
public function setLocked(bool $locked): self
{
$this->data['locked'] = $locked;

return $this;
}

/**
* Whether anyone in the thread may add others to it. Private threads only.
*/
public function setInvitable(bool $invitable): self
{
$this->data['invitable'] = $invitable;

return $this;
}

/**
* How long the thread sits idle before archiving itself, in minutes.
* Discord takes 60, 1440, 4320 or 10080.
*/
public function setAutoArchiveDuration(int $minutes): self
{
$this->data['auto_archive_duration'] = $minutes;

return $this;
}
}
12 changes: 12 additions & 0 deletions tests/Rest/ChannelTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
use Tempcord\Discord\Rest\Helpers\Channel\Channel\GuildStageVoiceChannelBuilder;
use Tempcord\Discord\Rest\Helpers\Channel\Channel\GuildTextChannelBuilder;
use Tempcord\Discord\Rest\Helpers\Channel\Channel\GuildVoiceChannelBuilder;
use Tempcord\Discord\Rest\Helpers\Channel\Channel\ThreadChannelBuilder;
use Tempcord\Discord\Rest\Helpers\Channel\EditMessageBuilder;
use Tempcord\Discord\Rest\Helpers\Channel\EditPermissionsBuilder;
use Tempcord\Discord\Rest\Helpers\Channel\MessageBuilder;
Expand Down Expand Up @@ -192,6 +193,17 @@ public static function httpBindingsProvider(): array
'returnType' => PartsChannel::class,
]
],
'Modify channel with Thread' => [
'method' => 'modify',
'args' => ['::channel id::', new ThreadChannelBuilder()],
'mockOptions' => [
'method' => 'patch',
'return' => (object) [],
],
'validationOptions' => [
'returnType' => PartsChannel::class,
]
],
'Delete channel' => [
'method' => 'delete',
'args' => ['::channel id::'],
Expand Down
81 changes: 81 additions & 0 deletions tests/Rest/Helpers/Channel/Channel/ThreadChannelBuilderTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php

declare(strict_types=1);

namespace Tests\Tempcord\Discord\Rest\Helpers\Channel\Channel;

use PHPUnit\Framework\TestCase;
use Tempcord\Discord\Rest\Helpers\Channel\Channel\ThreadChannelBuilder;

class ThreadChannelBuilderTest extends TestCase
{
public function testItAppliesForumTags(): void
{
$builder = ThreadChannelBuilder::new()->setAppliedTags(['::voting::', '::urgent::']);

$this->assertSame(['applied_tags' => ['::voting::', '::urgent::']], $builder->get());
$this->assertSame(['::voting::', '::urgent::'], $builder->getAppliedTags());
}

/**
* Discord replaces the whole set, so the keys have to be a plain list — an
* array with gaps in it serialises to an object and the call is refused.
*/
public function testTagsAreSentAsAList(): void
{
$tags = [3 => '::voting::', 7 => '::urgent::'];

$this->assertSame(
'{"applied_tags":["::voting::","::urgent::"]}',
json_encode(ThreadChannelBuilder::new()->setAppliedTags($tags)->get()),
);
}

public function testTakingEveryTagOffIsSaidWithAnEmptyList(): void
{
$this->assertSame(['applied_tags' => []], ThreadChannelBuilder::new()->setAppliedTags([])->get());
}

public function testItArchivesAndLocks(): void
{
$builder = ThreadChannelBuilder::new()->setArchived(true)->setLocked(true);

$this->assertSame(['archived' => true, 'locked' => true], $builder->get());
}

public function testItSetsTheIdleTimeBeforeArchiving(): void
{
$this->assertSame(
['auto_archive_duration' => 1440],
ThreadChannelBuilder::new()->setAutoArchiveDuration(1440)->get(),
);
}

public function testItSetsWhoMayAddOthers(): void
{
$this->assertSame(['invitable' => false], ThreadChannelBuilder::new()->setInvitable(false)->get());
}

public function testItSetsSlowmode(): void
{
$this->assertSame(
['rate_limit_per_user' => 30],
ThreadChannelBuilder::new()->setRateLimitPerUser(30)->get(),
);
}

public function testItRenames(): void
{
$this->assertSame(['name' => '::title::'], ThreadChannelBuilder::new()->setName('::title::')->get());
}

/**
* A thread has no channel type of its own to send, unlike every other
* builder here — Discord infers it from the channel being modified, and
* sending one is refused.
*/
public function testItSendsNoChannelType(): void
{
$this->assertArrayNotHasKey('type', ThreadChannelBuilder::new()->setName('::title::')->get());
}
}