-
Notifications
You must be signed in to change notification settings - Fork 5
feat: Create automatically a thread on discord when creating a reposi… #66
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from 1 commit
5bfc199
d26f359
e3dd70d
783c25b
0a778b1
2f45789
d10cc1a
11a4163
a160ce4
8e3f652
349388e
6ba9adb
3fbbfb8
a29a85b
21d7bf9
a3b27a6
e468d03
e83788d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| <?php | ||
|
|
||
| namespace App\Actions\Discord\Channels; | ||
|
|
||
| use ReflectionException; | ||
| use Saloon\Contracts\Response; | ||
| use App\Enums\Discord\ChannelType; | ||
| use App\Actions\Discord\DiscordAction; | ||
| use Saloon\Exceptions\PendingRequestException; | ||
| use Saloon\Exceptions\InvalidResponseClassException; | ||
| use App\Http\Integrations\Discord\Channel\Requests\CreateGuildChannel; | ||
|
|
||
| class CreateChannel | ||
| { | ||
| use DiscordAction; | ||
|
|
||
| /** | ||
| * @throws InvalidResponseClassException | ||
| * @throws ReflectionException | ||
| * @throws PendingRequestException | ||
| */ | ||
| public function execute( | ||
| string $name, | ||
| ChannelType $channelType = ChannelType::TEXT, | ||
| ?string $parentId = null | ||
| ): Response { | ||
| $request = new CreateGuildChannel( | ||
| name: $name, | ||
| channelType: $channelType, | ||
| parentId: $parentId, | ||
| ); | ||
|
|
||
| return $this->connector->send($request); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| <?php | ||
|
|
||
| namespace App\Actions\Discord\Channels; | ||
|
|
||
| use Exception; | ||
| use App\Models\Channel; | ||
| use App\Models\Repository; | ||
| use Illuminate\Support\Str; | ||
| use App\DTO\Discord\ChannelData; | ||
| use App\Enums\Discord\ChannelType; | ||
| use Illuminate\Database\Eloquent\Model; | ||
|
|
||
| class CreateThreadFromRepository | ||
| { | ||
| public function execute(Repository $repository): Model|bool | ||
| { | ||
| try { | ||
| /** @var ChannelData $threadData */ | ||
| $threadData = app(CreateChannel::class)->execute( | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is it ok to create a forum on discord per repository?!
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes ! but we have to re work this pr, lot of change since and i want to make a lot of new thing. My idea was : One Forum per repo and one thread into the forum per pr |
||
| name: $this->generateChannelName($repository->full_name), | ||
| channelType: ChannelType::FORUM, | ||
| parentId: config('services.discord.channels.code_reviews'), | ||
| )->dtoOrFail(); | ||
|
|
||
| return $repository->channels()->save( | ||
| new Channel([ | ||
| 'channel_id' => $threadData->channelId, | ||
| 'parent_id' => $threadData->parentId, | ||
| 'guild_id' => $threadData->guildId, | ||
| 'type' => $threadData->type, | ||
| 'name' => $threadData->name, | ||
| ]), | ||
| ); | ||
| } catch (Exception) { | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| private function generateChannelName(string $name): string | ||
| { | ||
| return Str::slug( | ||
| title: Str::replace( | ||
| search: '/', | ||
| replace: '-', | ||
| subject: $name, | ||
| ), | ||
| ); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| <?php | ||
|
|
||
| namespace App\Actions\Discord; | ||
|
|
||
| use App\Http\Integrations\Discord\DiscordAPIConnector; | ||
|
|
||
| trait DiscordAction | ||
| { | ||
| protected DiscordAPIConnector $connector; | ||
|
|
||
| public function __construct() | ||
| { | ||
| $this->connector = new DiscordAPIConnector; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| <?php | ||
|
|
||
| namespace App\Actions\Github\Repository; | ||
|
|
||
| use Exception; | ||
| use App\Models\Webhook; | ||
| use App\Models\Repository; | ||
| use App\Actions\Github\GithubAction; | ||
| use Illuminate\Support\Facades\Hash; | ||
| use Illuminate\Database\Eloquent\Model; | ||
|
|
||
| class CreateWebhookFromRepository | ||
| { | ||
| use GithubAction; | ||
|
|
||
| public function execute(Repository $repository): Model|bool | ||
| { | ||
| try { | ||
| $webhookData = app(CreateWebhook::class)->execute( | ||
| username: $repository->username, | ||
| repository: $repository->repository, | ||
| secret: Hash::make($repository->node_id), | ||
| )->dtoOrFail(); | ||
|
|
||
| return $repository->webhooks()->save( | ||
| new Webhook([ | ||
| 'title' => $repository->full_name . ' hook', | ||
| 'hook_id' => $webhookData->id, | ||
| ]), | ||
| ); | ||
| } catch (Exception) { | ||
| return false; | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| <?php | ||
|
|
||
| namespace App\DTO\Discord; | ||
|
|
||
| use Spatie\LaravelData\Data; | ||
| use App\Enums\Discord\ChannelType; | ||
| use Spatie\LaravelData\Attributes\MapInputName; | ||
| use Spatie\LaravelData\Mappers\SnakeCaseMapper; | ||
| use Spatie\LaravelData\Attributes\MapOutputName; | ||
|
|
||
| #[MapInputName(SnakeCaseMapper::class)] | ||
| #[MapOutputName(SnakeCaseMapper::class)] | ||
| class ChannelData extends Data | ||
| { | ||
| public function __construct( | ||
| #[MapInputName('id')] | ||
| public string $channelId, | ||
| public ChannelType $type, | ||
| public string $name, | ||
| public ?string $parentId, | ||
| public string $guildId, | ||
| ) {} | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| <?php | ||
|
|
||
| namespace App\Enums\Discord; | ||
|
|
||
| /** | ||
| * @link https://discord.com/developers/docs/resources/channel#channel-object-channel-types Documentation | ||
| */ | ||
| enum ChannelType: int | ||
| { | ||
| case TEXT = 0; // a text channel within a server | ||
| case DM = 1; // a direct message between users | ||
| case VOICE = 2; // a voice channel within a server | ||
| case GROUP_DM = 3; // a direct message between multiple users | ||
| case CATEGORY = 4; // an organizational category that contains up to 50 channels | ||
| case ANNOUNCEMENT = 5; // a channel that users can follow and crosspost into their own server (formerly news channels) | ||
| case ANNOUNCEMENT_THREAD = 10; // a temporary sub-channel within a ANNOUNCEMENT channel | ||
| case PUBLIC_THREAD = 11; // a temporary sub-channel within a TEXT or FORUM channel | ||
| case PRIVATE_THREAD = 12; // a temporary sub-channel within a TEXT channel that is only viewable by those invited and those with the MANAGE_THREADS permission | ||
| case STAGE_VOICE = 13; // a voice channel for hosting events with an audience | ||
| case DIRECTORY = 14; // the channel in a hub containing the listed servers | ||
| case FORUM = 15; // Channel that can only contain threads | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| <?php | ||
|
|
||
| namespace App\Http\Integrations\Discord\Channel\Requests; | ||
|
|
||
| use Saloon\Enums\Method; | ||
| use Saloon\Http\Request; | ||
| use Saloon\Contracts\Response; | ||
| use App\DTO\Discord\ChannelData; | ||
| use Saloon\Contracts\Body\HasBody; | ||
| use App\Enums\Discord\ChannelType; | ||
| use Saloon\Traits\Body\HasJsonBody; | ||
|
|
||
| class CreateGuildChannel extends Request implements HasBody | ||
| { | ||
| use HasJsonBody; | ||
|
|
||
| /** | ||
| * Define the HTTP method | ||
| * | ||
| * @var Method | ||
| */ | ||
| protected Method $method = Method::POST; | ||
|
|
||
| public function __construct( | ||
| public string $name, | ||
| public ChannelType $channelType = ChannelType::TEXT, | ||
| public string|int|null $parentId = null, | ||
| ) {} | ||
|
|
||
| /** | ||
| * Define the endpoint for the request | ||
| * | ||
| * @return string | ||
| */ | ||
| public function resolveEndpoint(): string | ||
| { | ||
| return '/guilds/' | ||
| . config('services.discord.guild_id') | ||
| . '/channels'; | ||
| } | ||
|
|
||
| protected function defaultBody(): array | ||
| { | ||
| return [ | ||
| 'name' => $this->name, | ||
| 'type' => $this->channelType->value, | ||
| 'parent_id' => $this->parentId, | ||
| ]; | ||
| } | ||
|
|
||
| /** | ||
| * @param Response $response | ||
| * | ||
| * @return ChannelData | ||
| */ | ||
| public function createDtoFromResponse(Response $response): ChannelData | ||
| { | ||
| return ChannelData::from($response->collect()); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| <?php | ||
|
|
||
| namespace App\Http\Integrations\Discord; | ||
|
|
||
| use Saloon\Http\Connector; | ||
| use Saloon\Traits\Plugins\AcceptsJson; | ||
| use Saloon\Http\Auth\TokenAuthenticator; | ||
| use Saloon\Traits\Plugins\AlwaysThrowOnErrors; | ||
|
|
||
| /** | ||
| * @link https://discord.com/developers/docs/intro Documentation | ||
| */ | ||
| class DiscordAPIConnector extends Connector | ||
| { | ||
| use AcceptsJson, AlwaysThrowOnErrors; | ||
|
|
||
| /** | ||
| * The Base URL of the API | ||
| * | ||
| * @return string | ||
| */ | ||
| public function resolveBaseUrl(): string | ||
| { | ||
| return (string)config('services.discord.base_path'); | ||
| } | ||
|
|
||
| /** | ||
| * @return string[] | ||
| */ | ||
| protected function defaultHeaders(): array | ||
| { | ||
| return [ | ||
| 'accept' => 'application/json', | ||
| 'Content-Type' => 'application/json', | ||
| ]; | ||
| } | ||
|
|
||
| /** | ||
| * @return TokenAuthenticator | ||
| */ | ||
| protected function defaultAuth(): TokenAuthenticator | ||
| { | ||
| return new TokenAuthenticator( | ||
| token: (string)config('services.discord.bot_token'), | ||
| prefix: 'Bot' | ||
| ); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| <?php | ||
|
|
||
| namespace App\Jobs\Discord\Channels; | ||
|
|
||
| use App\Models\Repository; | ||
| use Illuminate\Bus\Queueable; | ||
| use Illuminate\Contracts\Queue\ShouldQueue; | ||
| use Illuminate\Foundation\Bus\Dispatchable; | ||
| use Illuminate\Queue\InteractsWithQueue; | ||
| use Illuminate\Queue\SerializesModels; | ||
| use App\Actions\Discord\Channels\CreateThreadFromRepository; | ||
|
|
||
| class CreateThreadFromRepositoryJob implements ShouldQueue | ||
| { | ||
| use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; | ||
|
|
||
| /** | ||
| * Create a new job instance. | ||
| */ | ||
| public function __construct( | ||
| protected Repository $repository | ||
| ) {} | ||
|
|
||
| /** | ||
| * Execute the job. | ||
| */ | ||
| public function handle(): void | ||
| { | ||
| app(CreateThreadFromRepository::class)->execute($this->repository); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| <?php | ||
|
|
||
| namespace App\Jobs\Github\Repository; | ||
|
|
||
| use App\Models\Repository; | ||
| use Illuminate\Bus\Queueable; | ||
| use Illuminate\Queue\SerializesModels; | ||
| use Illuminate\Queue\InteractsWithQueue; | ||
| use Illuminate\Contracts\Queue\ShouldQueue; | ||
| use Illuminate\Foundation\Bus\Dispatchable; | ||
| use App\Actions\Github\Repository\CreateWebhookFromRepository; | ||
|
|
||
| class CreateWebhookFromRepositoryJob implements ShouldQueue | ||
| { | ||
| use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; | ||
|
|
||
| /** | ||
| * Create a new job instance. | ||
| */ | ||
| public function __construct( | ||
| protected Repository $repository | ||
| ) {} | ||
|
|
||
| /** | ||
| * Execute the job. | ||
| */ | ||
| public function handle(): void | ||
| { | ||
| app(CreateWebhookFromRepository::class)->execute($this->repository); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is your opinion on union types?
I think its more readable and clear using it, like null|string
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for null its ok to use "?"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
string|int|null ok
bcuz ?string|int not readable and may not work