diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index fac55927..55f86929 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -7,6 +7,7 @@ use App\Jobs\ProcessSessionReminders; use App\Jobs\ProcessSoloCertExpiryWarnings; use App\Jobs\ProcessShanwickController; +use App\Jobs\DiscordTrainingUpdates; use App\Models\Roster\RosterMember; use App\Notifications\Network\OneWeekInactivityReminder; use App\Notifications\Network\TwoWeekInactivityReminder; @@ -104,6 +105,9 @@ protected function schedule(Schedule $schedule) //Training/OTS session reminders $schedule->job(new ProcessSessionReminders())->daily(); + // Check Training Threads Status (Once per week) + $schedule->job(new DiscordTrainingUpdates())->weeklyOn(7, '17:00'); + // Discord role updating //$schedule->job(new UpdateDiscordUserRoles)->twiceDaily(6, 18); } diff --git a/app/Http/Controllers/DiscordTestController.php b/app/Http/Controllers/DiscordTestController.php index 66585945..6e442408 100644 --- a/app/Http/Controllers/DiscordTestController.php +++ b/app/Http/Controllers/DiscordTestController.php @@ -4,7 +4,7 @@ use Illuminate\Http\Request; use App\Services\DiscordClient; -use App\Jobs\ProcessShanwickController; +use App\Jobs\DiscordTrainingUpdates; class DiscordTestController extends Controller { @@ -26,7 +26,7 @@ public function EditTagTest() public function Shanwick() { // Dispatch the job - $job = ProcessShanwickController::dispatch(); + $job = DiscordTrainingUpdates::dispatch(); // Call the handle method directly to get the result synchronously $result = $job->handle(); diff --git a/app/Http/Controllers/Training/ApplicationsController.php b/app/Http/Controllers/Training/ApplicationsController.php index 7922eea2..4425e687 100644 --- a/app/Http/Controllers/Training/ApplicationsController.php +++ b/app/Http/Controllers/Training/ApplicationsController.php @@ -144,7 +144,7 @@ public function applyPost(Request $request) $discord = new DiscordClient(); $discord->sendMessageWithEmbed(config('app.env') == 'local' ? intval(config('services.discord.web_logs')) : intval(config('services.discord.applications')), 'New Training Applicant!', $application->user->fullName('FLC').' has just applied to control at Gander Oceanic! - [View their application now](https://ganderoceanic.ca/admin/training/applications/'.$application->reference_id.')'); +[View their application now](https://ganderoceanic.ca/admin/training/applications/'.$application->reference_id.')'); //Redirect to application page return redirect()->route('training.applications.show', $application->reference_id); diff --git a/app/Http/Controllers/Training/InstructingController.php b/app/Http/Controllers/Training/InstructingController.php index 4982d79f..1f7b17b3 100644 --- a/app/Http/Controllers/Training/InstructingController.php +++ b/app/Http/Controllers/Training/InstructingController.php @@ -64,7 +64,7 @@ public function instructors() public function students() { //Get all students - $students = Student::whereCurrent(true)->orderBy('created_at', 'desc')->get(); + $students = Student::whereCurrent(true)->orderBy('created_at', 'asc')->get(); $pastStudents = Student::whereCurrent(false)->orderBy('updated_at', 'desc')->get(); //Return view @@ -600,19 +600,22 @@ public function assignStudentToInstructor(Request $request, $student_id) $link->student_id = $student->id; $link->save(); - //If student has ready for pick up, remove and add In Progress + //Relabelling process foreach ($student->labels as $label) { - //Find label - if (strtolower($label->label()->name) == 'ready for pick-up') { - $label->delete(); + $label->delete(); + } + + //Assign it with link + $link = new StudentStatusLabelLink([ + 'student_id' => $student->id, + 'student_status_label_id' => StudentStatusLabel::whereName('In Progress')->first()->id, + ]); + $link->save(); - //Assign it with link - $link = new StudentStatusLabelLink([ - 'student_id' => $student->id, - 'student_status_label_id' => StudentStatusLabel::whereName('In Progress')->first()->id, - ]); - $link->save(); - } + // Update Thread Tag + if ($student->user->hasDiscord() && $student->user->member_of_czqo) { + $discord = new DiscordClient(); + $discord->EditThreadTag('In Progress', $student->user->id); } //Notify instructor @@ -646,6 +649,12 @@ public function dropStudentFromInstructor($student_id) ]); $link->save(); + // Update Thread Tag + if ($student->user->hasDiscord() && $student->user->member_of_czqo) { + $discord = new DiscordClient(); + $discord->EditThreadTag('Ready For Pick-Up', $student->user->id); + } + //Discord notification in instructors channel $discord = new DiscordClient(); $discord->sendMessageWithEmbed(intval(config('services.discord.instructors')), 'A new student is available for pick-up by an Instructor', $student->user->fullName('FLC').' is available to be picked up by an instructor!'); diff --git a/app/Jobs/DiscordTrainingUpdates.php b/app/Jobs/DiscordTrainingUpdates.php new file mode 100644 index 00000000..cd92a60a --- /dev/null +++ b/app/Jobs/DiscordTrainingUpdates.php @@ -0,0 +1,231 @@ +getClient()->get('channels/'.env('DISCORD_TRAINING_FORUM').'/threads/archived/public'); + $results = json_decode($response->getBody(), true); + + // dd($results); + + foreach($results['threads'] as $thread){ + $archiveTimestamp = Carbon::parse($thread['thread_metadata']['archive_timestamp']); + + // Thread was closed within the last 10 days + if ($archiveTimestamp >= Carbon::now()->subDays(10) && $archiveTimestamp <= Carbon::now()) { + // Your code to handle the condition + // dd($archiveTimestamp); + + // Get the ID of the Training Thread Recently Closed + if (preg_match('/\d+$/', $thread['name'], $matches)) { + $cid = $matches[0]; + } else { + $cid = null; + } + + // See if CID is still a student + $student = Student::where('user_id', $cid)->first(); + if($student !== null){ + + // Thread should be active, so lets activate it. + $discord = new DiscordClient(); + $data = $discord->getClient()->patch('channels/'.$thread['id'], [ + 'json' => [ + 'locked' => false, + 'archived' => false, + ] + ]); + } + } + } + + $discord->sendMessageWithEmbed(env('DISCORD_WEB_LOGS'), 'AUTO: Training Thread Opened',$counter. ' Threads were automatically reopended as they had expired (more than 1 week since last activity)'); + } + + // Function for Training Thread Availability Updates + { + // Initialize the DiscordClient inside the handle method + $discord = new DiscordClient(); + + // Number of Messages Sent + $counter = 0; + + // Get Active Threads + $response = $discord->getClient()->get('guilds/'.env('DISCORD_GUILD_ID').'/threads/active'); + $results = json_decode($response->getBody(), true); + + foreach ($results['threads'] as $thread) { + + // Get the ID of the Active Training Thread + if (preg_match('/\d+$/', $thread['name'], $matches)) { + $cid = $matches[0]; + } else { + $cid = null; + } + + // Check Lable is 'In Progress' or 'Ready For Pick-Up' + + // Check Sessions Upcoming + $student = Student::where('user_id', $cid)->first(); + $upcoming_sessions = TrainingSession::where('student_id', $student->id)->whereBetween('scheduled_time', [Carbon::now(), Carbon::now()->addDays(7)])->first(); + + if($upcoming_sessions == null){ + // There is no sessions within the next week + $counter++; //Add 1 to the $counter variable + + // SendEmbed to ask student to send availability + $discord->sendEmbedInTrainingThread($cid, "Your Availability", 'Hello, <@'.$student->user->discord_user_id.'> + +Please provide your availability for the next 7-14 days. Please ensure to tag the `@Instructor` role with all times you are available. Please provide these times in Zulu Format. + +One of our team will make contact with you to organise a session if they have availability matching yours. + +*If you have done this in the past few days, please disregard this message.*'); + } + } + + // Tell the log chat + $discord->sendMessageWithEmbed(env('DISCORD_WEB_LOGS'), 'AUTO: Training Thread Availability Requests',$counter. ' Training Threads have been messaged asking for their weekly availability. This is only completed if a student has no scheduled session within the next 7 days.'); + } + + // Check 'Awaiting Exam' label students between 31-37 Days after Application + { + $count = 0; + + $student = Student::whereBetween('created_at', [Carbon::now()->subDays(37), Carbon::now()->subDays(30)])->where('current', true)->get(); + + foreach($student as $s){ + if ($s->hasLabel('Awaiting Exam')) { + // Add one to the count + $count++; + // SendEmbed to ask student to send availability + $discord->sendEmbedInTrainingThread($cid, "Exam Not Requested", 'Hello, <@'.$s->user->discord_user_id.'> + +Our records indicate that you have not requested, or completed your exam within a month of your Application being approved. + +Please read the above message in order to understand how to request the exam. + +Should you not request, and pass the exam within 60 days of your application being accepted, your training will be automatically terminated, and you will need to reapply to begin your training once more. + +**Kind Regards, +Gander Oceanic Training Team**'); + } + } + + // Tell the log chat + $discord->sendMessageWithEmbed(env('DISCORD_WEB_LOGS'), 'AUTO: Training Thread Exam Requests Reminder',$count. ' Students are between 30-37 days past application without completing the exam. They have been notified of this.'); + } + + + // Check 'Awaiting-Exam' label students 60 Days after Application and Terminate Training Automatically + { + $count2 = 0; + + $student = Student::where('created_at', '<=', Carbon::now()->subDays(60))->where('current', true)->get(); + + foreach($student as $s){ + if ($s->hasLabel('Awaiting Exam')) { + // Add one to the count + $count2++; + + // dd($s); + + //Make as not current + $s->current = false; + + //Remove role + $s->user->removeRole('Student'); + + //Discord Updates + if ($s->user->hasDiscord() && $s->user->member_of_czqo) { + //Get Discord client + $discord = new DiscordClient(); + + //remove student discord role + $discord->removeRole($s->user->discord_user_id, 482824058141016075); + + $discord->EditThreadTag('Inactive', $s->user->id); + + //close training Thread + $discord->closeTrainingThread($s->user->id, $s->user->discord_user_id, 'terminate'); + + // Notify Senior Team that new training has been terminated. + $discord->sendMessageWithEmbed(config('app.env') == 'local' ? intval(config('services.discord.web_logs')) : intval(config('services.discord.instructors')), 'Training Terminated', $s->user->fullName('FLC').' has had their training terminated. `Exam not completed within 60 days.`', 'error'); + + } else { + //Get Discord client + $discord = new DiscordClient(); + + // Notify Senior Team that training has been terminated + $discord->sendMessageWithEmbed(config('app.env') == 'local' ? intval(config('services.discord.web_logs')) : intval(config('services.discord.instructors')), 'Training Terminated', $s->user->fullName('FLC').' has had their training terminated. `Exam not completed within 60 days.`', 'error'); + } + + foreach ($s->labels as $label) { + if (!in_array($label->label()->name, ['inactive'])) { + $label->delete(); + } + } + + //Remove labels and instructor links and availability + $label = new StudentStatusLabelLink([ + 'student_status_label_id' => StudentStatusLabel::whereName('Inactive')->first()->id, + 'student_id' => $s->id, + ]); + $label->save(); + + + if ($s->instructor()) { + $s->instructor()->delete(); + } + foreach ($s->availability as $a) { + $a->delete(); + } + + //notify + $s->user->notify(new RemovedAsStudent()); + + //Save + $s->save(); + } + } + + // Tell the log chat + $discord->sendMessageWithEmbed(env('DISCORD_WEB_LOGS'), 'AUTO: Training Termination',$count2. ' Students have been terminated automatically. Exam not completed within 60 days.'); + + } + } +} diff --git a/app/Services/DiscordClient.php b/app/Services/DiscordClient.php index add3690a..08f1cfde 100644 --- a/app/Services/DiscordClient.php +++ b/app/Services/DiscordClient.php @@ -22,6 +22,11 @@ public function __construct() ]); } + // Get Discord Client (For external use outside of this php file) + public function getClient() { + return $this->client; + } + public function sendMessage($channelId, $message) { $response = $this->client->post("channels/{$channelId}/messages", [ @@ -91,6 +96,7 @@ public function createTrainingThread($name, $user) $response = $this->client->post("channels/".env('DISCORD_TRAINING_FORUM')."/threads", [ 'json' => [ 'name' => $name, + 'auto_archive_duration' => 20160, 'applied_tags' => [1271845980865695774], //Tag ID for 'New Request' 'message' => [ 'content' => $user.', your application has now been approved. Welcome to Gander Oceanic! @@ -132,16 +138,34 @@ public function closeTrainingThread($cid, $discord_id, $status) $this->sendMessageWithEmbed($thread['id'], 'Oceanic Training Completed!', 'Congratulations <@'.$discord_id.'>, you have now been certified on Gander & Shanwick Oceanic! -This training thread will now be closed due to the completion of your training. Your discord roles will automatically be updated within the next 24 Hours. +This training thread will now be closed due to the completion of your training. Your discord roles will automatically be updated within the next 24 If you have any questions, please reach out to your Instructor, or ask your question in <#836707337829089322>. -Enjoy controlling Gander & Shanwick OCA!'); +Enjoy controlling Gander & Shanwick OCA! + +**Kind Regards, +Gander Oceanic Training Team**'); + } elseif($status == "cancel") { $this->sendMessageWithEmbed($thread['id'], 'Oceanic Training Cancelled', -'<@'.$discord_id.'>, Your training request with Gander Oceanic has been terminated on timestamp.':F> +'<@'.$discord_id.'>, Your training request with Gander Oceanic has been terminated at timestamp.':F> + +If you would like to begin training again, please re-apply via the Gander Website. + +**Kind Regards, +Gander Oceanic Training Team**'); + + } elseif($status == "terminate"){ + $this->sendMessageWithEmbed($thread['id'], 'Oceanic Training Terminated', +'<@'.$discord_id.'>, Your training request with Gander Oceanic has been terminated at timestamp.':F>. + +This is due to not completing the Exam within 60 Days of your application being accepted. + +If you would like to begin training again, please re-apply via the Gander Website. -If you would like to begin training again, please re-apply via the Gander Website.'); +**Kind Regards, +Gander Oceanic Training Team**'); } // Lock and Archive the Thread diff --git a/resources/views/admin/training/instructing/students/student.blade.php b/resources/views/admin/training/instructing/students/student.blade.php index 304b438b..f202877f 100644 --- a/resources/views/admin/training/instructing/students/student.blade.php +++ b/resources/views/admin/training/instructing/students/student.blade.php @@ -58,7 +58,7 @@
    @can('edit students') @endcan @if($student->user->rosterProfile->certification == "training") diff --git a/resources/views/my/index.blade.php b/resources/views/my/index.blade.php index daf16f91..becd036b 100644 --- a/resources/views/my/index.blade.php +++ b/resources/views/my/index.blade.php @@ -411,11 +411,11 @@ class="waves-effect list-group-item list-group-item-action"> class="waves-effect list-group-item list-group-item-action"> Roster - Solo Certifications - + --}} @endcan @can('view applications')