Skip to content

Commit

Permalink
feat: discord bot integration (#683)
Browse files Browse the repository at this point in the history
  • Loading branch information
Kyrch authored Jun 1, 2024
1 parent cbd7fe7 commit 80ed802
Show file tree
Hide file tree
Showing 29 changed files with 1,013 additions and 81 deletions.
7 changes: 2 additions & 5 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -253,13 +253,10 @@ MAILGUN_SECRET=
MAILGUN_ENDPOINT=api.mailgun.net
POSTMARK_TOKEN=
DISCORD_BOT_API_TOKEN=
DISCORD_BOT_API_URL=
DISCORD_BOT_API_KEY=
DB_UPDATES_DISCORD_CHANNEL=
ADMIN_DISCORD_CHANNEL=
SUBMISSIONS_DISCORD_CHANNEL=
WINTER_DISCORD_FORUM_TAG=
SPRING_DISCORD_FORUM_TAG=
SUMMER_DISCORD_FORUM_TAG=
FALL_DISCORD_FORUM_TAG=
MAL_CLIENT_ID=null
OPENAI_BEARER_TOKEN=null

Expand Down
7 changes: 2 additions & 5 deletions .env.example-sail
Original file line number Diff line number Diff line change
Expand Up @@ -254,13 +254,10 @@ MAILGUN_SECRET=
MAILGUN_ENDPOINT=api.mailgun.net
POSTMARK_TOKEN=
DISCORD_BOT_API_TOKEN=
DISCORD_BOT_API_URL=
DISCORD_BOT_API_KEY=
DB_UPDATES_DISCORD_CHANNEL=
ADMIN_DISCORD_CHANNEL=
SUBMISSIONS_DISCORD_CHANNEL=
WINTER_DISCORD_FORUM_TAG=
SPRING_DISCORD_FORUM_TAG=
SUMMER_DISCORD_FORUM_TAG=
FALL_DISCORD_FORUM_TAG=
MAL_CLIENT_ID=null
OPENAI_BEARER_TOKEN=null

Expand Down
81 changes: 22 additions & 59 deletions app/Actions/Discord/DiscordThreadAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,8 @@

namespace App\Actions\Discord;

use App\Constants\Config\ServiceConstants;
use App\Enums\Discord\EmbedColor;
use App\Enums\Models\Wiki\AnimeSeason;
use App\Enums\Models\Wiki\ImageFacet;
use App\Models\Discord\DiscordThread;
use App\Models\Wiki\Anime;
use App\Models\Wiki\Image;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Http;
Expand All @@ -27,60 +23,27 @@ class DiscordThreadAction
*/
public function handle(Anime $anime, array $fields): void
{
$name = Arr::get($fields, 'name');
$anime->load(Anime::RELATION_IMAGES);

/** @var \Illuminate\Filesystem\FilesystemAdapter */
$imageDisk = Storage::disk(Config::get('image.disk'));

$imagePath = $anime->images()->where(Image::ATTRIBUTE_FACET, ImageFacet::COVER_LARGE)->first()->path;

$animepage = json_decode(file_get_contents(base_path('composer.json')), true)['homepage'].'anime/';
$description = '**Synopsis:** '.strip_tags($anime->synopsis)."\n\n".'**Link:** '.$animepage.$anime->slug;

Http::withToken(Config::get('services.discord.token'), 'Bot')
->asMultipart()
->attach('file', file_get_contents($imageDisk->url($imagePath)), 'image.jpg')
->post("https://discord.com/api/v10/channels/{$this->getDiscordChannel()}/threads", [
'payload_json' => json_encode([
'name' => $name,
'applied_tags' => $this->getAppliedTags($anime->season->value),
'message' => [
'embeds' => [
[
'color' => EmbedColor::PURPLE->value,
'title' => $anime->name,
'description' => $description,
]
],
]
])
])->throw();
}

/**
* Get Discord forum channel the thread will be created to.
*
* @return string
*/
protected function getDiscordChannel(): string
{
return Config::get(ServiceConstants::SUBMISSIONS_DISCORD_CHANNEL_QUALIFIED);
}
$anime->name = Arr::get($fields, 'name');

/**
* Get the IDs of the tags applied to the thread.
*
* @param int $season
* @return array
*/
protected function getAppliedTags(int $season): array
{
return match ($season) {
AnimeSeason::WINTER->value => [Config::get('services.discord.submissions_forum_tags.winter')],
AnimeSeason::SPRING->value => [Config::get('services.discord.submissions_forum_tags.spring')],
AnimeSeason::SUMMER->value => [Config::get('services.discord.submissions_forum_tags.summer')],
AnimeSeason::FALL->value => [Config::get('services.discord.submissions_forum_tags.fall')],
default => [],
};
/** @var \Illuminate\Filesystem\FilesystemAdapter */
$fs = Storage::disk(Config::get('image.disk'));

$anime->images->each(fn ($image) => Arr::set($image, 'link', $fs->url($image->path)));

$response = Http::withHeaders(['x-api-key' => Config::get('services.discord.api_key')])
->acceptJson()
->post(Config::get('services.discord.api_url') . '/thread', $anime->toArray())
->throw()
->json();

if (Arr::has($response, 'id')) {
DiscordThread::query()->create([

Check failure on line 42 in app/Actions/Discord/DiscordThreadAction.php

View workflow job for this annotation

GitHub Actions / static-analysis

Property 'anime_id' does not exist in App\Models\Discord\DiscordThread model.
DiscordThread::ATTRIBUTE_NAME => Arr::get($response, 'name'),
DiscordThread::ATTRIBUTE_ID => intval(Arr::get($response, 'id')),
DiscordThread::ATTRIBUTE_ANIME => $anime->getKey(),
]);
}
}
}
}
65 changes: 65 additions & 0 deletions app/Actions/Discord/DiscordVideoNotificationAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

declare(strict_types=1);

namespace App\Actions\Discord;

use App\Models\Wiki\Image;
use App\Models\Wiki\Video;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Storage;

class DiscordVideoNotificationAction
{
/**
* Handle the action.
*
* @param Collection<int, Video> $videos
* @param array $fields
*
* @return void
*/
public function handle(Collection $videos, array $fields): void
{
$type = Arr::get($fields, 'type');

/** @var \Illuminate\Filesystem\FilesystemAdapter */
$fs = Storage::disk(Config::get('image.disk'));

$newVideos = [];

foreach ($videos as $video) {
$video
->load([
'animethemeentries.animetheme.anime.discordthread',
'animethemeentries.animetheme.anime.images',
'animethemeentries.animetheme.group',
'animethemeentries.animetheme.song.artists',
]);

$theme = $video->animethemeentries->first()->animetheme;

if ($theme->anime->discordthread === null) continue;

Arr::set($video, 'source_name', $video->source->localize());
Arr::set($video, 'overlap_name', $video->overlap->localize());
Arr::set($theme, 'type_name', $theme->type->localize());

$theme->anime->images->each(function (Image $image) use ($fs) {
Arr::set($image, 'link', $fs->url($image->path));
});

$newVideos[] = $video;
}

Http::withHeaders(['x-api-key' => Config::get('services.discord.api_key')])
->post(Config::get('services.discord.api_url') . '/notification', [
'type' => $type,
'videos' => $newVideos,
])
->throw();
}
}
62 changes: 62 additions & 0 deletions app/Events/Discord/DiscordThread/DiscordThreadDeleted.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

declare(strict_types=1);

namespace App\Events\Discord\DiscordThread;

use App\Events\Base\Admin\AdminDeletedEvent;
use App\Models\Discord\DiscordThread;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Http;

/**
* Class DiscordThreadDeleted.
*
* @extends AdminDeletedEvent<DiscordThread>
*/
class DiscordThreadDeleted extends AdminDeletedEvent
{
/**
* Create a new event instance.
*
* @param DiscordThread $thread
*/
public function __construct(DiscordThread $thread)
{
parent::__construct($thread);
$thread->forceDelete();
$this->deleteThread();
}

/**
* Get the model that has fired this event.
*
* @return DiscordThread
*/
public function getModel(): DiscordThread
{
return $this->model;
}

/**
* Get the description for the Discord message payload.
*
* @return string
*/
protected function getDiscordMessageDescription(): string
{
return "Discord Thread '**{$this->getModel()->getName()}**' has been deleted.";
}

/**
* Delete the thread on discord.
*
* @return void
*/
protected function deleteThread(): void
{
Http::withHeaders(['x-api-key' => Config::get('services.discord.api_key')])
->delete(Config::get('services.discord.api_url') . '/thread', ['id' => $this->getModel()->getKey()])
->throw();
}
}
62 changes: 62 additions & 0 deletions app/Events/Discord/DiscordThread/DiscordThreadUpdated.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

declare(strict_types=1);

namespace App\Events\Discord\DiscordThread;

use App\Events\Base\Admin\AdminUpdatedEvent;
use App\Models\Discord\DiscordThread;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Http;

/**
* Class DiscordThreadUpdated.
*
* @extends AdminUpdatedEvent<DiscordThread>
*/
class DiscordThreadUpdated extends AdminUpdatedEvent
{
/**
* Create a new event instance.
*
* @param DiscordThread $thread
*/
public function __construct(DiscordThread $thread)
{
parent::__construct($thread);
$this->updateThread();
$this->initializeEmbedFields($thread);
}

/**
* Get the model that has fired this event.
*
* @return DiscordThread
*/
public function getModel(): DiscordThread
{
return $this->model;
}

/**
* Get the description for the Discord message payload.
*
* @return string
*/
protected function getDiscordMessageDescription(): string
{
return "Discord Thread '**{$this->getModel()->getName()}**' has been updated.";
}

/**
* Update the thread on discord.
*
* @return void
*/
protected function updateThread(): void
{
Http::withHeaders(['x-api-key' => Config::get('services.discord.api_key')])
->put(Config::get('services.discord.api_url') . '/thread', $this->getModel())

Check failure on line 59 in app/Events/Discord/DiscordThread/DiscordThreadUpdated.php

View workflow job for this annotation

GitHub Actions / static-analysis

Parameter #2 $data of method Illuminate\Http\Client\PendingRequest::put() expects array, App\Models\Discord\DiscordThread given.
->throw();
}
}
36 changes: 36 additions & 0 deletions app/Filament/BulkActions/BaseBulkAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

declare(strict_types=1);

namespace App\Filament\BulkActions;

use App\Models\BaseModel;
use Filament\Tables\Actions\BulkAction;
use Illuminate\Database\Eloquent\Collection;

/**
* Class BaseBulkAction.
*/
abstract class BaseBulkAction extends BulkAction
{
/**
* Initial setup for the action.
*
* @return void
*/
protected function setUp(): void
{
parent::setUp();

$this->action(fn (Collection $records, array $data) => $this->handle($records, $data));
}

/**
* Handle the action.
*
* @param Collection<int, BaseModel> $records
* @param array $fields
* @return void
*/
abstract public function handle(Collection $records, array $fields): void;
}
Loading

0 comments on commit 80ed802

Please sign in to comment.