├── .editorconfig
├── .env.example
├── .gitattributes
├── .gitignore
├── .idea
├── .gitignore
├── blade.xml
├── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
├── dataSources.xml
├── laracord.iml
├── laravel-idea-personal.xml
├── laravel-idea.xml
├── material_theme_project_new.xml
├── modules.xml
├── php.xml
└── vcs.xml
├── .prettierrc
├── LICENSE.md
├── README.md
├── app
├── Actions
│ ├── Mentors
│ │ ├── Help
│ │ │ ├── MentorHelpAction.php
│ │ │ └── MentorHelpException.php
│ │ ├── Join
│ │ │ ├── MentorJoinAction.php
│ │ │ └── MentorJoinException.php
│ │ ├── MentorCommandInterface.php
│ │ ├── MentorCommandsEnum.php
│ │ └── MentorDTO.php
│ └── Teams
│ │ ├── Join
│ │ ├── JoinTeamAction.php
│ │ ├── JoinTeamDTO.php
│ │ └── JoinTeamException.php
│ │ └── Spawn
│ │ └── SpawnTeamAction.php
├── Bot.php
├── Commands
│ └── SpawnRoomsCommand.php
├── Console
│ └── Commands
│ │ └── LoadHackathonCommand.php
├── Enums
│ ├── TeamNicheEnum.php
│ └── TeamRoleEnum.php
├── Events
│ └── GuildJoinEvent.php
├── Exceptions
│ └── CommandException.php
├── Models
│ ├── Guild.php
│ ├── Mentor
│ │ └── Mentor.php
│ ├── Team
│ │ ├── Member.php
│ │ └── Team.php
│ └── User.php
├── Providers
│ └── BotServiceProvider.php
└── SlashCommands
│ ├── JoinTeamCommand.php
│ ├── MentorCommand.php
│ ├── OverviewCommand.php
│ └── PingCommand.php
├── artisan
├── bootstrap
└── app.php
├── box.json
├── composer.json
├── composer.lock
├── config
├── app.php
├── bot.php
├── database.php
└── discord.php
└── database
├── migrations
├── 0001_01_01_000000_create_users_table.php
├── 0002_01_01_000000_create_personal_access_tokens_table.php
├── 2024_05_15_154342_create_teams_table.php
├── 2024_05_15_154840_create_members_table.php
├── 2024_05_21_234316_create_mentors_table.php
└── 2024_05_22_193613_create_guilds_table.php
└── seeders
├── DatabaseSeeder.php
├── GuildsSeeder.php
└── TeamsSeeder.php
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | insert_final_newline = true
7 | indent_style = space
8 | indent_size = 2
9 | trim_trailing_whitespace = true
10 | quote_type = single
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
15 | [*.php]
16 | indent_size = 4
17 |
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | APP_NAME=Laracord
2 | APP_ENV=development
3 |
4 | DISCORD_TOKEN=
5 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto eol=lf
2 | /.github export-ignore
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor
2 | /builds
3 | /cache
4 | *.sqlite
5 | /database/database.sqlite
6 |
7 | .env
8 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Editor-based HTTP Client requests
5 | /httpRequests/
6 | # Datasource local storage ignored files
7 | /dataSources/
8 | /dataSources.local.xml
9 |
--------------------------------------------------------------------------------
/.idea/blade.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/dataSources.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | sqlite.xerial
6 | true
7 | org.sqlite.JDBC
8 | jdbc:sqlite:$PROJECT_DIR$/database/database.sqlite
9 | $ProjectFileDir$
10 |
11 |
12 | mysql.8
13 | true
14 | com.mysql.cj.jdbc.Driver
15 | jdbc:mysql://localhost:3306
16 | $ProjectFileDir$
17 |
18 |
19 |
--------------------------------------------------------------------------------
/.idea/laracord.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
--------------------------------------------------------------------------------
/.idea/laravel-idea-personal.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/laravel-idea.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/.idea/material_theme_project_new.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/php.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "semi": false,
3 | "singleQuote": true,
4 | "trailingComma": "es5"
5 | }
6 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) Brandon Nifong
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Discord Hacka Server
2 |
3 | *
4 |
--------------------------------------------------------------------------------
/app/Actions/Mentors/Help/MentorHelpAction.php:
--------------------------------------------------------------------------------
1 | sendHelpRequest($dto);
20 |
21 | return $this->respondWithMessage();
22 | }
23 |
24 |
25 | private function sendHelpRequest(MentorDTO $dto)
26 | {
27 | $server = app('bot')->discord();
28 | $guild = $server
29 | ->guilds
30 | ->get('id', config('bot.main_guild'));
31 |
32 | $channel = $guild
33 | ->channels
34 | ->find(fn($channel) => str_contains($channel->name, 'pedidos-de-ajuda'));
35 |
36 | $member = Member::where('discord_id', $dto->member->id)->first();
37 | if (!$member) {
38 | throw MentorHelpException::notRegistered();
39 | }
40 |
41 |
42 | $mentorType = $dto->args->pull('tipo-mentoria')->value;
43 | if ($mentorType === 'outro') {
44 | $discordId = "1241470842462928987";
45 | } else {
46 | $discordId = TeamRoleEnum::from($mentorType)->getDiscordId();
47 | }
48 |
49 | $mentorTag = sprintf('<@&%s>', $discordId);
50 | $channelRedirect = sprintf('<#%s>', $member->team->channels_ids[1]);
51 | dump($channelRedirect);
52 |
53 | $messageBuilder = Message::make(null)
54 | ->title('Pedido de Mentoria')
55 | ->content('**Contexto**: ' . $dto->args->pull('contexto')->value)
56 | ->field('Time', $member->team_id)
57 | ->field('Link Canal', $channelRedirect)
58 | ->field('Mentor Requisitado', $mentorTag)
59 | ->field('Mentorado', '<@' . $dto->member->id . '>')
60 | ->info()
61 | ->timestamp();
62 |
63 | /** @var \Discord\Parts\Channel\Message $message */
64 | $message = await($channel->sendMessage($messageBuilder->build()));
65 |
66 | // emotion eyes
67 | await($message->react('✅'));
68 | // emotion check
69 | await($message->react('👀'));
70 | }
71 |
72 | public function respondWithMessage(): MessageBuilder
73 | {
74 | return Message::make(app('bot'))
75 | ->content('Você requisitou a ajuda de um mentor! Em breve alguém te contatará via chat ou irá entrar na sua sala de voz.')
76 | ->build();
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/app/Actions/Mentors/Help/MentorHelpException.php:
--------------------------------------------------------------------------------
1 | where('email', $dto->args->pull('email')->value)
18 | ->orWhere('provider_id', $dto->member->id)
19 | ->first();
20 |
21 | if (!$mentor) {
22 | throw MentorJoinException::notFound();
23 | }
24 |
25 | if ($mentor->accepted_at) {
26 | throw MentorJoinException::alreadyAccepted();
27 | }
28 |
29 | $mentor->acceptInvite($dto);
30 | $this->addRole($dto);
31 |
32 | return $this->respondWithMessage();
33 | }
34 |
35 | private function addRole(MentorDTO $dto): void
36 | {
37 |
38 | $alreadyHasARole = $dto->member->roles->find(
39 | fn($role) => $role->name === 'Pessoa Mentora'
40 | );
41 |
42 | if ($alreadyHasARole) {
43 | return;
44 | }
45 |
46 |
47 | $mentorRole = $dto->guild->roles->find(
48 | fn($role) => $role->name === 'Pessoa Mentora'
49 | );
50 | await($dto->member->addRole($mentorRole));
51 | }
52 |
53 | private function respondWithMessage(): MessageBuilder
54 | {
55 | return Message::make(app('bot'))
56 | ->content('You have requested help from a mentor!')
57 | ->build();
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/app/Actions/Mentors/Join/MentorJoinException.php:
--------------------------------------------------------------------------------
1 | app(MentorJoinAction::class),
18 | self::Help => app(MentorHelpAction::class)
19 | };
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/Actions/Mentors/MentorDTO.php:
--------------------------------------------------------------------------------
1 | guild,
24 | member: $interaction->member,
25 | args: $interaction->data->options->first()->options
26 | );
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/Actions/Teams/Join/JoinTeamAction.php:
--------------------------------------------------------------------------------
1 | 'geral',
16 | 'category' => Channel::TYPE_TEXT
17 | ],
18 | // [
19 | // 'name' => 'links-uteis',
20 | // 'category' => Channel::TYPE_TEXT
21 | // ],
22 | [
23 | 'name' => 'Voz',
24 | 'category' => Channel::TYPE_VOICE
25 | ],
26 | ];
27 |
28 | public function __construct(
29 | private readonly Team $team,
30 | private readonly TeamMember $teamMember
31 | )
32 | {
33 | }
34 |
35 | public function handle(JoinTeamDTO $dto): Team
36 | {
37 | $memberIsAlreadyInATeam = $this->teamMember->alreadyJoinedATeam($dto->member->id);
38 | if ($memberIsAlreadyInATeam) {
39 | $inviteUrl = $memberIsAlreadyInATeam->team->guild->invite_url;
40 | throw JoinTeamException::alreadyInATeam($inviteUrl);
41 | }
42 |
43 | $team = $this->team->findByOwnerEmail($dto->teamKey);
44 |
45 | if (!$team) {
46 | throw JoinTeamException::teamCodeNotExists($dto);
47 | }
48 |
49 |
50 | if (!$team->guild_id) {
51 | $this->setGuildToTeam($team);
52 | }
53 |
54 | $team->addMember($dto);
55 | $this->manageRoles($dto);
56 |
57 | return $team;
58 | }
59 |
60 | private function manageRoles(JoinTeamDTO $dto): void
61 | {
62 | $teamlessRole = $dto->member->guild->roles->find(fn($role) => $role->name === 'Sem Time');
63 | $teamedRole = $dto->member->guild->roles->find(fn($role) => $role->name === 'Em Time');
64 |
65 | $hasTeamRole = $dto->member->roles->find(fn($role) => $role->name === 'Em Time');
66 | if (!$hasTeamRole) {
67 | await($dto->member->addRole($teamedRole));
68 | }
69 |
70 | $hasTeamlessRole = $dto->member->roles->find(fn($role) => $role->name === 'Sem Time');
71 | if ($hasTeamlessRole) {
72 | await($dto->member->removeRole($teamlessRole));
73 | }
74 |
75 | }
76 |
77 | private function setGuildToTeam(Team $team): void
78 | {
79 | $guild = Guild::query()
80 | ->where('main_server', false)
81 | ->where('teams_count', '<', config('bot.teamsPerGuild'))
82 | ->first();
83 |
84 | $team->update(['guild_id' => $guild->provider_id]);
85 | }
86 |
87 | }
88 |
--------------------------------------------------------------------------------
/app/Actions/Teams/Join/JoinTeamDTO.php:
--------------------------------------------------------------------------------
1 | member,
25 | selectedMemberRoleType: TeamRoleEnum::from($interaction->data->options->pull('area')->value),
26 | teamKey: $interaction->data->options->pull('chave')->value,
27 | githubUsername: $interaction->data->options->pull('github')?->value,
28 | );
29 |
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/Actions/Teams/Join/JoinTeamException.php:
--------------------------------------------------------------------------------
1 | teamKey));
22 | }
23 |
24 | public static function teamAlreadyFull(): self
25 | {
26 | return new self('Esse time já está cheio!');
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/Actions/Teams/Spawn/SpawnTeamAction.php:
--------------------------------------------------------------------------------
1 | 'geral',
17 | 'category' => Channel::TYPE_TEXT
18 | ],
19 | // [
20 | // 'name' => 'links-uteis',
21 | // 'category' => Channel::TYPE_TEXT
22 | // ],
23 | [
24 | 'name' => 'Voz',
25 | 'category' => Channel::TYPE_VOICE
26 | ],
27 | ];
28 |
29 | public function handle(Team $team)
30 | {
31 | /** @var Guild $guild */
32 | $guild = app('bot')
33 | ->discord()
34 | ->guilds
35 | ->get('id', config('bot.main_guild'));
36 |
37 | $this->createRole($team, $guild);
38 |
39 | $payload = $this->buildBaseCategory($guild, $team);
40 | $category = await($guild->channels->save($payload));
41 | $this->createChannels($team, $guild, $category);
42 | }
43 |
44 |
45 | private function createChannels(Team $team, Guild $guild, Channel $channel): void
46 | {
47 | $team = $team->refresh();
48 | $categoryId = $channel->id;
49 | $mentorId = $guild->roles->find(fn(Role $role) => $role->name === 'Pessoa Mentora')->id;
50 | $roleId = $team->role_id;
51 | $everyoneRole = config('bot.everyoneRole');
52 |
53 | $channelsToUpdate = [$channel->id];
54 | foreach ($this->newChannels as $newChannel) {
55 |
56 | $channel = $guild->channels->create([
57 | 'name' => $newChannel['name'],
58 | 'parent_id' => $categoryId,
59 | 'type' => $newChannel['category'],
60 | 'permission_overwrites' => [
61 | [
62 | "id" => $everyoneRole,
63 | "type" => 0,
64 | "allow" => '0',
65 | "deny" => '1049600'
66 | ],
67 | [
68 | "id" => $mentorId,
69 | "type" => 0,
70 | "allow" => '1049600',
71 | "deny" => '0'
72 | ],
73 | [
74 | "id" => $roleId,
75 | "type" => 0,
76 | "allow" => '1049600',
77 | "deny" => '0'
78 | ]
79 | ],
80 | ]);
81 |
82 | /** @var Channel $fuckingChannel */
83 | $fuckingChannel = await($guild->channels->save($channel));
84 | $channelsToUpdate[] = $fuckingChannel->id;
85 | }
86 |
87 | $team->update(['channels_ids' => $channelsToUpdate]);
88 | }
89 |
90 | private function defaultPermissions(Guild $guild): array
91 | {
92 | return [
93 | "id" => $guild->roles->first()->id,
94 | "type" => 0,
95 | "allow" => "0",
96 | "deny" => "1049600"
97 | ];
98 | }
99 |
100 | private function buildBaseCategory(Guild $guild, Team $team): Part
101 | {
102 | return $guild->channels->create([
103 | 'name' => 'Time ' . $team->getKey(),
104 | 'type' => Channel::TYPE_CATEGORY,
105 | 'permission_overwrites' => [
106 | $this->defaultPermissions($guild)
107 | ],
108 | ]);
109 | }
110 |
111 | private function createRole(Team $team, Guild $guild): Role
112 | {
113 | $roleDTO = $guild->roles->create([
114 | 'name' => 'Time ' . $team->getKey(),
115 | 'color' => 0x00FF00,
116 | 'hoist' => false,
117 | 'mentionable' => true,
118 | ]);
119 |
120 | $role = await($guild->roles->save($roleDTO));
121 | $team->updateRole($role->id);
122 |
123 | return $role;
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/app/Bot.php:
--------------------------------------------------------------------------------
1 | group(function () {
19 | // Route::get('/', fn () => 'Hello world!');
20 | });
21 |
22 | Route::middleware('api')->group(function () {
23 | // Route::get('/commands', fn () => collect($this->registeredCommands)->map(fn ($command) => [
24 | // 'signature' => $command->getSignature(),
25 | // 'description' => $command->getDescription(),
26 | // ]));
27 | });
28 | }
29 |
30 | public function afterBoot(): void
31 | {
32 | $activity = $this->discord()->factory(Activity::class, [
33 | 'type' => Activity::TYPE_PLAYING,
34 | 'name' => 'VS Code.',
35 | ]);
36 |
37 | $this->discord()->updatePresence($activity);
38 |
39 | // $allowedGuilds = array_keys(config('bot.guilds'));
40 | // $this
41 | // ->discord
42 | // ->guilds
43 | // ->filter(fn ($guild) => !in_array($guild->id, $allowedGuilds))
44 | // ->map(fn (Guild $guild) => $guild->leave());
45 |
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/app/Commands/SpawnRoomsCommand.php:
--------------------------------------------------------------------------------
1 | discord();
57 |
58 | /** @var Guild $guild */
59 | $guild = $discord->guilds->get('id', config('bot.main_guild'));
60 |
61 | $teams = Team::whereNotNull('guild_id')->get();
62 |
63 | foreach ($guild->members as $member) {
64 | /** @var Member $member */
65 | dump("dm sent to: " . $member->username);
66 |
67 | $member->sendMessage(
68 | Message::make(null)
69 | ->title("Aviso de migração de servidores")
70 | ->info()
71 | ->content("
72 | Olá participante da Maratona Tech pelo RS.
73 |
74 | Realizamos uma manutenção no nosso servidor de Discord e migramos todos os canais de equipes para nosso servidor principal, com o objetivo de facilitar e centralizar a comunicação com todas as equipes, bem como o atendimento de mentoria.
75 |
76 | Por favor, pedimos que caso você tenha alguma dificuldade ou faltar alguém da sua equipe, abra um ticket no canal #suporte e iremos te ajudar.
77 |
78 | Agradecemos a sua paciência e dedicação até aqui!
79 |
80 | --------------------------------------------------------------------
81 | Nome do servidor principal: Maratona Tech pelo RS
82 | Link para abrir um chamado de suporte: https://discord.gg/EtXtHg74
83 | --------------------------------------------------------------------
84 | ")
85 | ->build()
86 | );
87 | }
88 |
89 | $this
90 | ->message()
91 | ->title('SpawnRoomsCommand')
92 | ->content('Updated')
93 | ->send($message);
94 | }
95 |
96 | public function handle($message, $args)
97 | {
98 | $discord = app('bot')->discord();
99 |
100 | /** @var \Discord\Parts\Guild\Guild $guild */
101 | $guild = $discord->guilds->get('id', '1241451165112205322');
102 |
103 | $listChannels = $guild->channels->filter(function ($channel) {
104 | return $channel->type === Channel::TYPE_TEXT;
105 | });
106 |
107 | // delete voice rooms
108 | /** @var Channel $voiceRoom */
109 |
110 |
111 | return;
112 | $teams = Team::whereNotNull('guild_id')->get();
113 |
114 | foreach ($teams as $team) {
115 |
116 | $hasTeamRole = $guild->roles->find(fn($role) => $role->id === $team->role_id);
117 |
118 | if ($hasTeamRole) {
119 | dump('Role already exists: ' . $team->id);
120 | continue;
121 | }
122 |
123 | dump('Role Missing: ' . $team->id);
124 | app(SpawnTeamAction::class)->handle($team);
125 |
126 | $teamMembers = $team->members;
127 | foreach ($teamMembers as $teamMember) {
128 | $member = $guild->members->get('id', $teamMember->discord_id);
129 | $this->addParticipantRoles($member);
130 | }
131 | dump('Team added: ' . $team->id);
132 |
133 |
134 | }
135 |
136 | $this
137 | ->message()
138 | ->title('SpawnRoomsCommand')
139 | ->content('Hello world!')
140 | ->send($message);
141 | }
142 |
143 |
144 | private function addParticipantRoles(Member $member): void
145 | {
146 | $teamMember = \App\Models\Team\Member::query()
147 | ->where('discord_id', $member->id)
148 | ->first();
149 |
150 | $hasRole = $member->roles->find(fn($role) => $role->id === $teamMember->team->role_id);
151 |
152 | if ($hasRole) {
153 | return;
154 | }
155 |
156 | try {
157 | await($member->addRole($teamMember->team->role_id));
158 | } catch (\Exception $e) {
159 | dump($e->getMessage());
160 | }
161 |
162 | }
163 | }
164 |
--------------------------------------------------------------------------------
/app/Console/Commands/LoadHackathonCommand.php:
--------------------------------------------------------------------------------
1 | option('participants'));
23 | $mentorsSpreadsheetUrl = str($this->option('mentors'));
24 |
25 | if (!$participantsSpreadsheetUrl->isUrl()) {
26 | $this->error("Invalid participants spreadsheet URL");
27 | return self::FAILURE;
28 | }
29 |
30 | if (!$mentorsSpreadsheetUrl->isUrl()) {
31 | $this->error("Invalid mentors spreadsheet URL");
32 | return self::FAILURE;
33 | }
34 |
35 | $this->wipeTeams();
36 | $this->loadGuilds();
37 | $this->loadParticipants($participantsSpreadsheetUrl);
38 | $this->loadMentors($mentorsSpreadsheetUrl);
39 |
40 | $this->info("vai caraio");
41 | return self::SUCCESS;
42 | }
43 |
44 | private function loadParticipants(Stringable $participantsSpreadsheetUrl): void
45 | {
46 |
47 | $participantsList = str(file_get_contents($participantsSpreadsheetUrl))
48 | ->explode(PHP_EOL)
49 | ->map(fn(string $participantEmail) => str($participantEmail)->lower());
50 |
51 | $this->info(sprintf("Loading %s participants from $participantsSpreadsheetUrl", count($participantsList)));
52 |
53 | $participantsList
54 | ->each(function (Stringable $participantEmail) {
55 | Team::query()->updateOrCreate([
56 | 'owner_email' => $participantEmail->toString()
57 | ], ['channels_ids' => []]);
58 | $this->info("Participant loaded");
59 | });
60 | }
61 |
62 | private function loadMentors(mixed $mentorsSpreadsheetUrl): void
63 | {
64 | $mentorsList = str(file_get_contents($mentorsSpreadsheetUrl))
65 | ->explode(PHP_EOL)
66 | ->map(fn(string $mentorEmail) => str($mentorEmail)->lower());
67 |
68 | $this->info(sprintf("Loading %s mentors from $mentorsSpreadsheetUrl", count($mentorsList)));
69 |
70 | $mentorsList
71 | ->each(function (Stringable $mentorEmail) {
72 | $this->info("Mentor loaded");
73 | Mentor::query()->updateOrCreate([
74 | 'email' => $mentorEmail->toString()
75 | ]);
76 | });
77 | }
78 |
79 | private function wipeTeams(): void
80 | {
81 | DB::statement('SET FOREIGN_KEY_CHECKS=0');
82 |
83 | Team::truncate();
84 | Member::truncate();
85 |
86 | DB::statement('SET FOREIGN_KEY_CHECKS=1');
87 | }
88 |
89 | private function loadGuilds(): void
90 | {
91 | Guild::truncate();
92 | foreach (config('bot.guilds') as $guild) {
93 | Guild::query()->create($guild);
94 | }
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/app/Enums/TeamNicheEnum.php:
--------------------------------------------------------------------------------
1 | 'Pre Disaster',
16 | self::During => 'During Disaster',
17 | self::Post => 'Post Disaster',
18 | };
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/Enums/TeamRoleEnum.php:
--------------------------------------------------------------------------------
1 | 'Pessoa Desenvolvedora',
19 | self::Artist => 'Pessoa Artista',
20 | self::Business => 'Pessoa de Negócios',
21 | self::Engineer => 'Pessoa Engenheira',
22 | self::Marketing => 'Pessoa de Marketing',
23 | self::HR => 'Pessoa de Recursos Humanos',
24 | self::Logistics => 'Pessoa de Logística/Mobilidade',
25 | };
26 | }
27 |
28 | public function getDiscordId(): string
29 | {
30 | return match ($this) {
31 | self::Developer => '1241469983188582441',
32 | self::Business => '1241470085160243351',
33 | self::Engineer => '1241470066361634956',
34 | self::Marketing, self::Artist => '1241470339699970088',
35 | self::HR => '1241470768391393320',
36 | self::Logistics => '1241470469945692301',
37 | };
38 |
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/Events/GuildJoinEvent.php:
--------------------------------------------------------------------------------
1 | where('provider_id', $member->guild_id)->doesntExist()) {
30 | return;
31 | }
32 | $this->addTeamlessRole($member);
33 | $this->addParticipantRoles($member);
34 | $this->addMentorRole($member);
35 | }
36 |
37 | private function addTeamlessRole(Member $member)
38 | {
39 | if ($member->guild_id === config('bot.main_guild')) {
40 | return;
41 | }
42 |
43 | $hasRole = $member->roles->find(fn($role) => $role->name === 'Sem Time');
44 |
45 | if ($hasRole) {
46 | return;
47 | }
48 |
49 | await($member->addRole(
50 | $member->guild->roles->find(fn($role) => $role->name === 'Sem Time')
51 | ));
52 | }
53 |
54 | private function addParticipantRoles(Member $member): void
55 | {
56 | $hasGuild = Guild::query()
57 | ->where('main_server', false)
58 | ->where('provider_id', $member->guild_id)
59 | ->exists();
60 |
61 | if (!$hasGuild) {
62 | return;
63 | }
64 |
65 | $teamMember = \App\Models\Team\Member::query()
66 | ->where('discord_id', $member->id)
67 | ->whereHas('team', fn(Builder $team) => $team->where('guild_id', $member->guild_id))
68 | ->first();
69 |
70 | if (!$teamMember) {
71 | await($member->sendMessage('Você não está em nenhum time. Volte pro servidor principal e rode o comando /entrar-time com o e-mail do seu lider!'));
72 | await($member->sendMessage('Após entrar no time, saia e entre do servidor de times para receber suas permissões!'));
73 | dump("Member {$member->id} not found in any team");
74 | return;
75 | }
76 |
77 | await($member->addRole($teamMember->team->role_id));
78 |
79 | }
80 |
81 | private function addMentorRole(Member $member): void
82 | {
83 | if ($member->guild_id === config('bot.main_guild')) {
84 | return;
85 | }
86 |
87 | $mentor = Mentor::query()
88 | ->where('provider_id', $member->id)
89 | ->first();
90 |
91 | if (!$mentor) {
92 | return;
93 | }
94 |
95 | await($member->addRole(
96 | $member->guild->roles->find(fn($role) => $role->name === 'Pessoa Mentora')
97 | ));
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/app/Exceptions/CommandException.php:
--------------------------------------------------------------------------------
1 | title('Houve um erro ao processar seu comando!')
15 | ->error()
16 | ->field('Mensagem', $this->getMessage(), true)
17 | ->timestamp()
18 | ->build();
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/Models/Guild.php:
--------------------------------------------------------------------------------
1 | 'timestamp',
20 | ];
21 | }
22 |
23 | public function acceptInvite(MentorDTO $dto): void
24 | {
25 | $this->update([
26 | 'accepted_at' => now(),
27 | 'provider_id' => $dto->member->user->id,
28 | ]);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/Models/Team/Member.php:
--------------------------------------------------------------------------------
1 | TeamRoleEnum::class,
21 | ];
22 |
23 | public function team(): BelongsTo
24 | {
25 | return $this->belongsTo(Team::class);
26 | }
27 |
28 | public function alreadyJoinedATeam(string $discordId): ?Member
29 | {
30 | return $this
31 | ->where('discord_id', $discordId)
32 | ->first();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/Models/Team/Team.php:
--------------------------------------------------------------------------------
1 | TeamNicheEnum::class,
32 | 'channels_ids' => 'array',
33 | ];
34 | }
35 |
36 | public function guild(): BelongsTo
37 | {
38 | return $this->belongsTo(Guild::class, 'guild_id', 'provider_id');
39 | }
40 |
41 | public function members(): HasMany
42 | {
43 | return $this->hasMany(Member::class, 'team_id');
44 | }
45 |
46 | public function hasMaxMembers(): bool
47 | {
48 | return $this->members_count >= 5;
49 | }
50 |
51 |
52 | public function addMember(JoinTeamDTO $dto): void
53 | {
54 | $this->members()->create([
55 | 'discord_id' => $dto->member->id,
56 | 'role_type' => $dto->selectedMemberRoleType,
57 | 'github_username' => $dto->member
58 | ]);
59 | }
60 |
61 | public function updateRole(string $roleId): void
62 | {
63 | $this->update([
64 | 'role_id' => $roleId
65 | ]);
66 | }
67 |
68 | public function findByOwnerEmail(string $code): ?Team
69 | {
70 | return $this->query()->where('owner_email', $code)->first();
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/app/Models/User.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | protected $fillable = [
18 | 'username',
19 | 'discord_id',
20 | 'is_admin',
21 | ];
22 |
23 | /**
24 | * The attributes that should be cast.
25 | *
26 | * @var array
27 | */
28 | protected $casts = [
29 | 'is_admin' => 'boolean',
30 | ];
31 |
32 | /**
33 | * The highlighted Discord ID.
34 | *
35 | * @return string
36 | */
37 | public function getHighlightAttribute()
38 | {
39 | return "<@{$this->discord_id}>";
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/Providers/BotServiceProvider.php:
--------------------------------------------------------------------------------
1 | commands([
20 | LoadHackathonCommand::class,
21 | ]);
22 | }
23 |
24 | /**
25 | * Register any application services.
26 | *
27 | * @return void
28 | */
29 | public function register()
30 | {
31 | parent::register();
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/SlashCommands/JoinTeamCommand.php:
--------------------------------------------------------------------------------
1 | member->user->username . ' - ' . $interaction->member->user->id . ' - ' . now()->toDateTimeString() . ' - ' . now()->diffForHumans());
68 | try {
69 | $message = DB::transaction(function () use ($interaction, $joinTeam, $spawnTeam) {
70 | $dto = JoinTeamDTO::makeFromInteraction($interaction);
71 | $team = $joinTeam->handle($dto)->refresh();
72 |
73 | if (!$this->hasChannelsAndRoles($team)) {
74 | dump("generating channels for team - {$team->id}...");
75 | $spawnTeam->handle($team);
76 | }
77 |
78 | return $this
79 | ->message()
80 | ->title('Olá Participante!')
81 | ->content("
82 | Você entrou no time {$team->id}!
83 |
84 | Acesse a sala <#{$team->channels_ids[1]}> para conversar com seus colegas.
85 | ")
86 | ->build();
87 | });
88 |
89 | } catch (CommandException $e) {
90 | $message = $e->buildErrorMessage();
91 | }
92 | $interaction->respondWithMessage($message, true);
93 | }
94 |
95 | private function getInvite(Team $team): Invite
96 | {
97 | $team = $team->refresh();
98 | $channelId = collect($team->channels_ids)->reverse()->first();
99 | $channel = $this->discord
100 | ->guilds
101 | ->find(fn(Guild $guild) => $guild->id == $team->guild_id)
102 | ->channels
103 | ->get('name', $channelId);
104 |
105 |
106 | $inviteDTO = $channel->invites->create([
107 | 'max_age' => 36000,
108 | 'max_uses' => 100,
109 | 'temporary' => false,
110 | ]);
111 |
112 | return await($channel->invites->save($inviteDTO));
113 | }
114 |
115 | public function options(): array
116 | {
117 | return [
118 | [
119 | 'name' => 'chave',
120 | 'description' => 'E-mail do lider ou código do time',
121 | 'type' => Command::MESSAGE,
122 | 'required' => true
123 | ],
124 | [
125 | 'name' => 'area',
126 | 'description' => 'Sua área de atuação',
127 | 'type' => Command::MESSAGE,
128 | 'required' => true,
129 | 'choices' => collect(TeamRoleEnum::cases())->map(fn(TeamRoleEnum $role) => [
130 | 'name' => $role->getDescription(),
131 | 'value' => $role->value
132 | ])->toArray()
133 | ],
134 | [
135 | 'name' => 'github',
136 | 'description' => 'Seu usuário do GitHub, caso tenha! Ex: danielhe4rt',
137 | 'type' => Command::MESSAGE,
138 | 'required' => false
139 | ]
140 | ];
141 | }
142 |
143 | private function hasChannelsAndRoles(Team $team): bool
144 | {
145 | // TODO: gerar time
146 | $guild = $this->discord()
147 | ->guilds
148 | ->get('id', config('bot.main_guild'));
149 |
150 | $hasChannels = $guild
151 | ->channels
152 | ->filter(fn($role) => in_array($role->id, $team->channels_ids))
153 | ->count();
154 |
155 |
156 | $hasRole = $guild
157 | ->roles
158 | ->filter(fn($role) => $role->id == $team->role_id)
159 | ->count();
160 |
161 | return $hasChannels && $hasRole;
162 | }
163 | }
164 |
--------------------------------------------------------------------------------
/app/SlashCommands/MentorCommand.php:
--------------------------------------------------------------------------------
1 | data->options->first()->name);
65 | $dto = MentorDTO::makeFromInteraction($interaction);
66 |
67 | try {
68 | $response = $command->getAction()->handle($dto);
69 | } catch (CommandException $e) {
70 | $response = $e->buildErrorMessage();
71 | }
72 |
73 | $interaction->respondWithMessage($response, true);
74 | }
75 |
76 |
77 | public function options(): array
78 | {
79 |
80 | return [
81 | [
82 | "type" => Option::SUB_COMMAND,
83 | "name" => "entrar",
84 | "description" => "Entrar como mentor.",
85 | "options" => [
86 | [
87 | "type" => Option::STRING,
88 | "name" => "email",
89 | "description" => "Seu endereço de email cadastrado como mentor.",
90 | "required" => true
91 | ]
92 | ]
93 | ],
94 | [
95 | "type" => Option::SUB_COMMAND,
96 | "name" => "pedir-ajuda",
97 | "description" => "Peça ajuda para nossos mentores de plantão!",
98 | "options" => [
99 | [
100 | "type" => Option::STRING,
101 | "name" => "tipo-mentoria",
102 | "description" => "Informe qual tipo de pessoa você precisa de mentoria.",
103 | "required" => true,
104 | "choices" => collect(TeamRoleEnum::cases())->map(fn(TeamRoleEnum $role) => [
105 | 'name' => $role->getDescription(),
106 | 'value' => $role->value
107 | ])->push(['name' => 'Geral', 'value' => 'outro'])
108 | ->toArray()
109 | ],
110 | [
111 | "type" => Option::STRING,
112 | "name" => "contexto",
113 | "description" => "Descreva seu desafio atual.",
114 | "required" => true
115 | ],
116 | ]
117 | ]
118 | ];
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/app/SlashCommands/OverviewCommand.php:
--------------------------------------------------------------------------------
1 | whereHas('members')
64 | ->count();
65 |
66 | $totalMembers = Member::query()
67 | ->count();
68 |
69 | $mentorsIds = Mentor::query()
70 | ->whereNotNull('provider_id')
71 | ->pluck('provider_id')
72 | ->toArray();
73 |
74 |
75 | $onlineMentors = $interaction
76 | ->guild
77 | ->members
78 | ->filter(function (\Discord\Parts\User\Member $member) use ($mentorsIds) {
79 | return in_array($member->user->id, $mentorsIds)
80 | && $member->status === 'online';
81 | })->count();
82 |
83 | $interaction->respondWithMessage(
84 | $this->message()
85 | ->title('Estatísticas Gerais')
86 | ->fields([
87 | 'Times Ativos' => $teamsWithOneOrMoreMembers,
88 | 'Participantes Ativos' => $totalMembers,
89 | 'Mentores Online' => $onlineMentors
90 | ])
91 | ->timestamp()
92 | ->build()
93 | , true
94 | );
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/app/SlashCommands/PingCommand.php:
--------------------------------------------------------------------------------
1 | message()
61 | ->content('Pong!')
62 | ->timestamp()
63 | ->build();
64 | $interaction->respondWithMessage($message, true);
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/artisan:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | make(Illuminate\Contracts\Console\Kernel::class);
34 |
35 | $status = $kernel->handle(
36 | $input = new Symfony\Component\Console\Input\ArgvInput,
37 | new Symfony\Component\Console\Output\ConsoleOutput
38 | );
39 |
40 | /*
41 | |--------------------------------------------------------------------------
42 | | Shutdown The Application
43 | |--------------------------------------------------------------------------
44 | |
45 | | Once Artisan has finished running, we will fire off the shutdown events
46 | | so that any final work may be done by the application before we shut
47 | | down the process. This is the last thing to happen to the request.
48 | |
49 | */
50 |
51 | $kernel->terminate($input, $status);
52 |
53 | exit($status);
54 |
--------------------------------------------------------------------------------
/bootstrap/app.php:
--------------------------------------------------------------------------------
1 | create();
6 |
--------------------------------------------------------------------------------
/box.json:
--------------------------------------------------------------------------------
1 | {
2 | "chmod": "0755",
3 | "directories": [
4 | "app",
5 | "bootstrap",
6 | "config",
7 | "vendor",
8 | "database"
9 | ],
10 | "files": [
11 | "composer.json"
12 | ],
13 | "exclude-composer-files": false,
14 | "compression": "GZ",
15 | "compactors": [
16 | "KevinGH\\Box\\Compactor\\Php",
17 | "KevinGH\\Box\\Compactor\\Json"
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "laracord/laracord",
3 | "type": "project",
4 | "description": "Create Discord bots harnessing the full power of Laravel.",
5 | "keywords": ["framework", "laravel", "discord"],
6 | "license": "MIT",
7 | "require": {
8 | "php": "^8.2",
9 | "illuminate/database": "^11.5",
10 | "laracord/framework": "^2.0"
11 | },
12 | "require-dev": {
13 | "fakerphp/faker": "^1.23",
14 | "laravel/pint": "^1.15"
15 | },
16 | "autoload": {
17 | "psr-4": {
18 | "App\\": "app/",
19 | "Database\\Factories\\": "database/factories/",
20 | "Database\\Seeders\\": "database/seeders/"
21 | }
22 | },
23 | "scripts": {
24 | "post-root-package-install": [
25 | "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
26 | ]
27 | },
28 | "config": {
29 | "preferred-install": "dist",
30 | "sort-packages": true,
31 | "optimize-autoloader": true,
32 | "allow-plugins": {
33 | "php-http/discovery": true
34 | }
35 | },
36 | "minimum-stability": "dev",
37 | "prefer-stable": true
38 | }
39 |
--------------------------------------------------------------------------------
/config/app.php:
--------------------------------------------------------------------------------
1 | env('APP_NAME', 'Laracord'),
17 |
18 | /*
19 | |--------------------------------------------------------------------------
20 | | Application Version
21 | |--------------------------------------------------------------------------
22 | |
23 | | This value determines the "version" your application is currently running
24 | | in. You may want to follow the "Semantic Versioning" - Given a version
25 | | number MAJOR.MINOR.PATCH when an update happens: https://semver.org.
26 | |
27 | */
28 |
29 | 'version' => app('git.version'),
30 |
31 | /*
32 | |--------------------------------------------------------------------------
33 | | Application Environment
34 | |--------------------------------------------------------------------------
35 | |
36 | | This value determines the "environment" your application is currently
37 | | running in. This may determine how you prefer to configure various
38 | | services the application utilizes. This can be overridden using
39 | | the global command line "--env" option when calling commands.
40 | |
41 | */
42 |
43 | 'env' => env('APP_ENV', 'production'),
44 |
45 | /*
46 | |--------------------------------------------------------------------------
47 | | Application Timezone
48 | |--------------------------------------------------------------------------
49 | |
50 | | Here you may specify the default timezone for your application, which
51 | | will be used by the PHP date and date-time functions. We have gone
52 | | ahead and set this to a sensible default for you out of the box.
53 | |
54 | */
55 |
56 | 'timezone' => env('APP_TIMEZONE', 'UTC'),
57 |
58 | /*
59 | |--------------------------------------------------------------------------
60 | | Autoloaded Service Providers
61 | |--------------------------------------------------------------------------
62 | |
63 | | The service providers listed here will be automatically loaded on the
64 | | request to your application. Feel free to add your own services to
65 | | this array to grant expanded functionality to your applications.
66 | |
67 | */
68 |
69 | 'providers' => [
70 | App\Providers\BotServiceProvider::class,
71 | ],
72 |
73 | ];
74 |
--------------------------------------------------------------------------------
/config/bot.php:
--------------------------------------------------------------------------------
1 | '1240314593373327422',
5 | 'main_guild' => '1240311895362375791',
6 | 'teamsPerGuild' => 100,
7 | 'roomsPerTeam' => 3,
8 | 'everyoneRole' => '1240311895362375791',
9 | 'guilds' => [
10 | '1240311895362375791' => [
11 | 'provider_id' => '1240311895362375791',
12 | 'main_server' => true,
13 | 'invite_url' => 'https://discord.gg/4cAHq4rwzU',
14 | ],
15 | '1241451165112205322' => [
16 | 'provider_id' => '1241451165112205322',
17 | 'main_server' => false,
18 | 'invite_url' => 'https://discord.gg/5urz8aJKRt',
19 | ],
20 | '1242919315674628228' => [
21 | 'provider_id' => '1242919315674628228',
22 | 'main_server' => false,
23 | 'invite_url' => 'https://discord.gg/kTA388TvAa',
24 | ],
25 | '1242919447702929408' => [
26 | 'provider_id' => '1242919447702929408',
27 | 'main_server' => false,
28 | 'invite_url' => 'https://discord.gg/UxRE3ugeAJ',
29 | ]
30 | ]
31 | ];
32 |
--------------------------------------------------------------------------------
/config/database.php:
--------------------------------------------------------------------------------
1 | env('DB_CONNECTION', 'mysql'),
20 |
21 | /*
22 | |--------------------------------------------------------------------------
23 | | Database Connections
24 | |--------------------------------------------------------------------------
25 | |
26 | | Below are all of the database connections defined for your application.
27 | | An example configuration is provided for each database system which
28 | | is supported by Laravel. You're free to add / remove connections.
29 | |
30 | */
31 |
32 | 'connections' => [
33 |
34 | 'sqlite' => [
35 | 'driver' => 'sqlite',
36 | 'url' => env('DB_URL'),
37 | 'database' => env('DB_DATABASE', database_path('database.sqlite')),
38 | 'prefix' => '',
39 | 'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
40 | ],
41 |
42 | 'mysql' => [
43 | 'driver' => 'mysql',
44 | 'url' => env('DB_URL'),
45 | 'host' => env('DB_HOST', '127.0.0.1'),
46 | 'port' => env('DB_PORT', '3306'),
47 | 'database' => env('DB_DATABASE', 'laravel'),
48 | 'username' => env('DB_USERNAME', 'root'),
49 | 'password' => env('DB_PASSWORD', ''),
50 | 'unix_socket' => env('DB_SOCKET', ''),
51 | 'charset' => env('DB_CHARSET', 'utf8mb4'),
52 | 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
53 | 'prefix' => '',
54 | 'prefix_indexes' => true,
55 | 'strict' => true,
56 | 'engine' => null,
57 | 'options' => extension_loaded('pdo_mysql') ? array_filter([
58 | PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
59 | ]) : [],
60 | ],
61 |
62 | 'mariadb' => [
63 | 'driver' => 'mariadb',
64 | 'url' => env('DB_URL'),
65 | 'host' => env('DB_HOST', '127.0.0.1'),
66 | 'port' => env('DB_PORT', '3306'),
67 | 'database' => env('DB_DATABASE', 'laravel'),
68 | 'username' => env('DB_USERNAME', 'root'),
69 | 'password' => env('DB_PASSWORD', ''),
70 | 'unix_socket' => env('DB_SOCKET', ''),
71 | 'charset' => env('DB_CHARSET', 'utf8mb4'),
72 | 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
73 | 'prefix' => '',
74 | 'prefix_indexes' => true,
75 | 'strict' => true,
76 | 'engine' => null,
77 | 'options' => extension_loaded('pdo_mysql') ? array_filter([
78 | PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
79 | ]) : [],
80 | ],
81 |
82 | 'pgsql' => [
83 | 'driver' => 'pgsql',
84 | 'url' => env('DB_URL'),
85 | 'host' => env('DB_HOST', '127.0.0.1'),
86 | 'port' => env('DB_PORT', '5432'),
87 | 'database' => env('DB_DATABASE', 'laravel'),
88 | 'username' => env('DB_USERNAME', 'root'),
89 | 'password' => env('DB_PASSWORD', ''),
90 | 'charset' => env('DB_CHARSET', 'utf8'),
91 | 'prefix' => '',
92 | 'prefix_indexes' => true,
93 | 'search_path' => 'public',
94 | 'sslmode' => 'prefer',
95 | ],
96 |
97 | 'sqlsrv' => [
98 | 'driver' => 'sqlsrv',
99 | 'url' => env('DB_URL'),
100 | 'host' => env('DB_HOST', 'localhost'),
101 | 'port' => env('DB_PORT', '1433'),
102 | 'database' => env('DB_DATABASE', 'laravel'),
103 | 'username' => env('DB_USERNAME', 'root'),
104 | 'password' => env('DB_PASSWORD', ''),
105 | 'charset' => env('DB_CHARSET', 'utf8'),
106 | 'prefix' => '',
107 | 'prefix_indexes' => true,
108 | // 'encrypt' => env('DB_ENCRYPT', 'yes'),
109 | // 'trust_server_certificate' => env('DB_TRUST_SERVER_CERTIFICATE', 'false'),
110 | ],
111 |
112 | ],
113 |
114 | /*
115 | |--------------------------------------------------------------------------
116 | | Migration Repository Table
117 | |--------------------------------------------------------------------------
118 | |
119 | | This table keeps track of all the migrations that have already run for
120 | | your application. Using this information, we can determine which of
121 | | the migrations on disk haven't actually been run on the database.
122 | |
123 | */
124 |
125 | 'migrations' => [
126 | 'table' => 'migrations',
127 | 'update_date_on_publish' => true,
128 | ],
129 |
130 | /*
131 | |--------------------------------------------------------------------------
132 | | Redis Databases
133 | |--------------------------------------------------------------------------
134 | |
135 | | Redis is an open source, fast, and advanced key-value store that also
136 | | provides a richer body of commands than a typical key-value system
137 | | such as Memcached. You may define your connection settings here.
138 | |
139 | */
140 |
141 | 'redis' => [
142 |
143 | 'client' => env('REDIS_CLIENT', 'phpredis'),
144 |
145 | 'options' => [
146 | 'cluster' => env('REDIS_CLUSTER', 'redis'),
147 | 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
148 | ],
149 |
150 | 'default' => [
151 | 'url' => env('REDIS_URL'),
152 | 'host' => env('REDIS_HOST', '127.0.0.1'),
153 | 'username' => env('REDIS_USERNAME'),
154 | 'password' => env('REDIS_PASSWORD'),
155 | 'port' => env('REDIS_PORT', '6379'),
156 | 'database' => env('REDIS_DB', '0'),
157 | ],
158 |
159 | 'cache' => [
160 | 'url' => env('REDIS_URL'),
161 | 'host' => env('REDIS_HOST', '127.0.0.1'),
162 | 'username' => env('REDIS_USERNAME'),
163 | 'password' => env('REDIS_PASSWORD'),
164 | 'port' => env('REDIS_PORT', '6379'),
165 | 'database' => env('REDIS_CACHE_DB', '1'),
166 | ],
167 |
168 | ],
169 |
170 | ];
171 |
--------------------------------------------------------------------------------
/config/discord.php:
--------------------------------------------------------------------------------
1 | env('DISCORD_BOT_DESCRIPTION', 'The Laracord Discord Bot.'),
19 |
20 | /*
21 | |--------------------------------------------------------------------------
22 | | Discord Token
23 | |--------------------------------------------------------------------------
24 | |
25 | | Here you may specify your Discord bot token. You can find it under the
26 | | "Bot" section of your Discord application. Make sure to keep this
27 | | token private and never share it with anyone for security.
28 | |
29 | */
30 |
31 | 'token' => env('DISCORD_TOKEN', ''),
32 |
33 | /*
34 | |--------------------------------------------------------------------------
35 | | Gateway Intents
36 | |--------------------------------------------------------------------------
37 | |
38 | | Here you may specify the gateway intents for your Discord bot. This
39 | | will tell Discord what events your bot should receive. Intents can be
40 | | enabled in the Discord developer application portal under:
41 | |
42 | | Settings > Bot > Privileged Gateway Intents
43 | |
44 | */
45 |
46 | 'intents' => Intents::getDefaultIntents() | Intents::MESSAGE_CONTENT | Intents::GUILD_MEMBERS,
47 |
48 | /*
49 | |--------------------------------------------------------------------------
50 | | Command Prefix
51 | |--------------------------------------------------------------------------
52 | |
53 | | Here you may specify the command prefix for the Discord bot. This
54 | | prefix will be used to distinguish commands from regular chat
55 | | messages. To use mentioning the bot as a prefix, use "@mention".
56 | | To use multiple prefixes, you may pass an array instead.
57 | |
58 | */
59 |
60 | 'prefix' => env('DISCORD_COMMAND_PREFIX', '!'),
61 |
62 | /*
63 | |--------------------------------------------------------------------------
64 | | Additional DiscordPHP Options
65 | |--------------------------------------------------------------------------
66 | |
67 | | Here you may specify any additional options for the DiscordPHP client.
68 | | These options will be passed directly to the DiscordPHP client.
69 | |
70 | | For more information, see the DiscordPHP documentation:
71 | | ↪
72 | |
73 | */
74 |
75 | 'options' => [
76 | 'loadAllMembers' => true,
77 | ],
78 |
79 | /*
80 | |--------------------------------------------------------------------------
81 | | HTTP Server
82 | |--------------------------------------------------------------------------
83 | |
84 | | The Laracord HTTP server allows you to receive and respond to HTTP
85 | | requests from the bot at the specified address/port. This can be useful
86 | | for creating a RESTful API for your bot.
87 | |
88 | | The HTTP server is automatically started when a `routes.php` file is
89 | | present and contains valid routes. You can override this behavior by
90 | | setting this option to `false`.
91 | |
92 | */
93 |
94 | 'http' => env('HTTP_SERVER', ':8080'),
95 |
96 | /*
97 | |--------------------------------------------------------------------------
98 | | Timestamp Format
99 | |--------------------------------------------------------------------------
100 | |
101 | | Here you may specify the timestamp format for the Discord bot. This
102 | | format will be used when formatting console output. You can set this
103 | | to `false` to disable timestamps.
104 | |
105 | */
106 |
107 | 'timestamp' => 'h:i:s A',
108 |
109 | /*
110 | |--------------------------------------------------------------------------
111 | | Bot Admins
112 | |--------------------------------------------------------------------------
113 | |
114 | | Here you may manually specify bot admins without using the User model.
115 | | These users will have access to all bot admin commands. User's must
116 | | be specified by their Discord user ID.
117 | |
118 | */
119 |
120 | 'admins' => [
121 | '247397456361291776', // Nadachi
122 | '204122995579551744' // danielhe4rt
123 | ],
124 |
125 | /*
126 | |--------------------------------------------------------------------------
127 | | Additional Commands
128 | |--------------------------------------------------------------------------
129 | |
130 | | Here you may specify any additional commands for the Discord bot. These
131 | | commands will be loaded in addition to the commands automatically loaded
132 | | in your project. By default, the Laracord-provided help command is
133 | | is registered here.
134 | |
135 | */
136 |
137 | 'commands' => [
138 | Laracord\Commands\HelpCommand::class,
139 | ],
140 |
141 | /*
142 | |--------------------------------------------------------------------------
143 | | Additional Services
144 | |--------------------------------------------------------------------------
145 | |
146 | | Here you may specify any additional services to run asynchronously
147 | | alongside the Discord bot. These services will be loaded in addition
148 | | to the services automatically loaded from your project.
149 | |
150 | */
151 |
152 | 'services' => [
153 | //
154 | ],
155 |
156 | /*
157 | |--------------------------------------------------------------------------
158 | | Additional Events
159 | |--------------------------------------------------------------------------
160 | |
161 | | Here you may specify any additional events to listen for in your
162 | | Discord bot. These events will be registered in addition to the
163 | | events automatically registered from your project.
164 | |
165 | */
166 |
167 | 'events' => [
168 | //
169 | ],
170 |
171 | ];
172 |
--------------------------------------------------------------------------------
/database/migrations/0001_01_01_000000_create_users_table.php:
--------------------------------------------------------------------------------
1 | id();
16 | $table->string('username')->index();
17 | $table->string('discord_id')->index();
18 | $table->boolean('is_admin')->default(false);
19 | $table->timestamps();
20 | });
21 | }
22 |
23 | /**
24 | * Reverse the migrations.
25 | */
26 | public function down(): void
27 | {
28 | Schema::dropIfExists('users');
29 | }
30 | };
31 |
--------------------------------------------------------------------------------
/database/migrations/0002_01_01_000000_create_personal_access_tokens_table.php:
--------------------------------------------------------------------------------
1 | id();
16 | $table->morphs('tokenable');
17 | $table->string('name');
18 | $table->string('token', 64)->unique();
19 | $table->text('abilities')->nullable();
20 | $table->timestamp('last_used_at')->nullable();
21 | $table->timestamp('expires_at')->nullable();
22 | $table->timestamps();
23 | });
24 | }
25 |
26 | /**
27 | * Reverse the migrations.
28 | */
29 | public function down(): void
30 | {
31 | Schema::dropIfExists('personal_access_tokens');
32 | }
33 | };
34 |
--------------------------------------------------------------------------------
/database/migrations/2024_05_15_154342_create_teams_table.php:
--------------------------------------------------------------------------------
1 | id();
13 | $table->string('guild_id')->nullable();
14 | $table->string('role_id')->nullable();
15 | $table->string('owner_email')->unique();
16 | $table->string('niche_type')->default(TeamNicheEnum::Unknown->value);
17 | $table->integer('members_count')->default(0);
18 | $table->string('channels_ids')->nullable();
19 | $table->timestamps();
20 | });
21 | }
22 |
23 | public function down(): void
24 | {
25 | Schema::dropIfExists('teams');
26 | }
27 | };
28 |
--------------------------------------------------------------------------------
/database/migrations/2024_05_15_154840_create_members_table.php:
--------------------------------------------------------------------------------
1 | id();
12 | $table->foreignId('team_id')
13 | ->constrained('teams')
14 | ->cascadeOnDelete();
15 | $table->string('discord_id')->unique();
16 | $table->string('role_type');
17 | $table->string('github_username')->nullable();
18 | $table->timestamps();
19 |
20 | $table->index('discord_id');
21 | });
22 | }
23 |
24 | public function down(): void
25 | {
26 | Schema::dropIfExists('members');
27 | }
28 | };
29 |
--------------------------------------------------------------------------------
/database/migrations/2024_05_21_234316_create_mentors_table.php:
--------------------------------------------------------------------------------
1 | id();
12 | $table->string('email');
13 | $table->string('provider_id')->nullable();
14 | $table->timestamp('accepted_at')->nullable();
15 | $table->timestamps();
16 | });
17 | }
18 |
19 | public function down(): void
20 | {
21 | Schema::dropIfExists('mentors');
22 | }
23 | };
24 |
--------------------------------------------------------------------------------
/database/migrations/2024_05_22_193613_create_guilds_table.php:
--------------------------------------------------------------------------------
1 | id();
12 | $table->string('provider_id');
13 | $table->boolean('main_server');
14 | $table->string('invite_url');
15 | $table->integer('teams_count')->default(0);
16 | $table->timestamps();
17 | });
18 | }
19 |
20 | public function down(): void
21 | {
22 | Schema::dropIfExists('guilds');
23 | }
24 | };
25 |
--------------------------------------------------------------------------------
/database/seeders/DatabaseSeeder.php:
--------------------------------------------------------------------------------
1 | call(GuildsSeeder::class);
16 | $this->call(TeamsSeeder::class);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/database/seeders/GuildsSeeder.php:
--------------------------------------------------------------------------------
1 | create($guild);
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/database/seeders/TeamsSeeder.php:
--------------------------------------------------------------------------------
1 | setTeamsPerGuild();
29 |
30 | $teams = range(1, 300);
31 |
32 | DB::statement('SET FOREIGN_KEY_CHECKS=0');
33 |
34 | Team::truncate();
35 | foreach ($teams as $team) {
36 | Team::create([
37 | 'owner_email' => sprintf('d+%s@d.com', $team),
38 | 'niche_type' => TeamNicheEnum::Unknown,
39 | 'members_count' => 0,
40 | 'channels_ids' => [],
41 | ]);
42 | }
43 | DB::statement('SET FOREIGN_KEY_CHECKS=1');
44 | }
45 |
46 | private function setTeamsPerGuild(): void
47 | {
48 | $this->teamsPerGuild = config('bot.teamsPerGuild');
49 | }
50 | }
51 |
--------------------------------------------------------------------------------