diff --git a/app/Events/ChatMessageSent.php b/app/Events/ChatMessageSent.php index ad7f18c..1d85bd6 100644 --- a/app/Events/ChatMessageSent.php +++ b/app/Events/ChatMessageSent.php @@ -25,6 +25,8 @@ public function __construct(ChatMessage $chatMessage) $this->message = [ 'id' => $chatMessage->id, 'content' => $chatMessage->content, + 'reply_to_id' => $chatMessage->reply_to_id, + 'reply_to_content' => $chatMessage->reply_to_content, 'created_at' => $chatMessage->created_at->toIso8601String(), 'user' => [ 'id' => $chatMessage->user->id, diff --git a/app/Events/ChatSettingsUpdated.php b/app/Events/ChatSettingsUpdated.php new file mode 100644 index 0000000..df3bea4 --- /dev/null +++ b/app/Events/ChatSettingsUpdated.php @@ -0,0 +1,49 @@ +settings = [ + 'enabled' => (bool) AppSetting::get('global_chat_enabled', true), + 'audience' => AppSetting::get('global_chat_audience', 'verified_members'), + 'disabled_reason' => (string) AppSetting::get('global_chat_disabled_reason', ''), + 'cooldown_seconds' => (int) AppSetting::get('global_chat_cooldown_seconds', 30), + 'max_messages' => (int) AppSetting::get('global_chat_max_messages', 200), + 'max_length' => (int) AppSetting::get('global_chat_max_length', 280), + ]; + } + + /** + * Get the channels the event should broadcast on. + * + * @return array + */ + public function broadcastOn(): array + { + return [ + new Channel('global-chat'), + ]; + } + + public function broadcastAs(): string + { + return 'settings.updated'; + } +} diff --git a/app/Http/Controllers/Admin/ChatSettingsController.php b/app/Http/Controllers/Admin/ChatSettingsController.php index 4a92065..421e1bb 100644 --- a/app/Http/Controllers/Admin/ChatSettingsController.php +++ b/app/Http/Controllers/Admin/ChatSettingsController.php @@ -5,6 +5,7 @@ use App\Http\Controllers\Controller; use App\Models\AppSetting; use App\Models\ChatMessage; +use App\Models\ChatReport; use Illuminate\Http\Request; use Inertia\Inertia; @@ -12,28 +13,97 @@ class ChatSettingsController extends Controller { public function edit() { + $bannedWords = AppSetting::get('global_chat_banned_words', ''); + $bannedWordsText = is_array($bannedWords) ? implode(', ', $bannedWords) : (string) $bannedWords; + return Inertia::render('admin/ChatSettings', [ 'settings' => [ 'enabled' => (bool) AppSetting::get('global_chat_enabled', true), 'audience' => AppSetting::get('global_chat_audience', 'verified_members'), + 'disabled_reason' => (string) AppSetting::get('global_chat_disabled_reason', ''), 'cooldown_seconds' => (int) AppSetting::get('global_chat_cooldown_seconds', 30), + 'max_messages' => (int) AppSetting::get('global_chat_max_messages', 200), + 'max_length' => (int) AppSetting::get('global_chat_max_length', 280), + 'profanity_filter_enabled' => (bool) AppSetting::get('global_chat_profanity_filter_enabled', true), + 'banned_words' => $bannedWordsText, ], 'totalMessages' => ChatMessage::count(), 'recentMessagesCount' => ChatMessage::where('created_at', '>=', now()->subHours(24))->count(), + 'pendingReportsCount' => ChatReport::where('status', 'pending')->count(), + ]); + } + + public function reports() + { + $reports = ChatReport::with(['reporter:id,name,username', 'reportedUser:id,name,username,chat_banned_until']) + ->latest('id') + ->take(100) + ->get() + ->map(fn ($report) => [ + 'id' => $report->id, + 'reporter_id' => $report->reporter_id, + 'reporter' => $report->reporter ? [ + 'id' => $report->reporter->id, + 'name' => $report->reporter->name, + 'username' => $report->reporter->username, + ] : null, + 'reported_user_id' => $report->reported_user_id, + 'reported_user_name' => $report->reported_user_name, + 'reported_user_username' => $report->reported_user_username, + 'reported_user' => $report->reportedUser ? [ + 'id' => $report->reportedUser->id, + 'name' => $report->reportedUser->name, + 'username' => $report->reportedUser->username, + 'chat_banned_until' => $report->reportedUser->chat_banned_until?->toIso8601String(), + 'is_chat_banned' => $report->reportedUser->isChatBanned(), + ] : null, + 'message_content' => $report->message_content, + 'message_sent_at' => $report->message_sent_at?->toIso8601String(), + 'reason' => $report->reason, + 'status' => $report->status, + 'created_at' => $report->created_at->toIso8601String(), + ]); + + return Inertia::render('admin/chat/Reports', [ + 'reports' => $reports, + 'pendingCount' => ChatReport::where('status', 'pending')->count(), + 'reviewedCount' => ChatReport::where('status', 'reviewed')->count(), + 'dismissedCount' => ChatReport::where('status', 'dismissed')->count(), ]); } public function update(Request $request) { $validated = $request->validate([ - 'enabled' => ['required', 'boolean'], + 'enabled' => ['nullable', 'boolean'], 'audience' => ['required', 'string', 'in:verified_members,all,disabled'], + 'disabled_reason' => ['nullable', 'string', 'max:255'], 'cooldown_seconds' => ['required', 'integer', 'min:0', 'max:3600'], + 'max_messages' => ['required', 'integer', 'min:20', 'max:1000'], + 'max_length' => ['required', 'integer', 'min:50', 'max:1000'], + 'profanity_filter_enabled' => ['required', 'boolean'], + 'banned_words' => ['nullable', 'string'], ]); - AppSetting::set('global_chat_enabled', $validated['enabled'], 'boolean'); + $isEnabled = $validated['audience'] !== 'disabled'; + AppSetting::set('global_chat_enabled', $isEnabled, 'boolean'); AppSetting::set('global_chat_audience', $validated['audience'], 'string'); + AppSetting::set('global_chat_disabled_reason', $validated['disabled_reason'] ?? '', 'string'); AppSetting::set('global_chat_cooldown_seconds', $validated['cooldown_seconds'], 'integer'); + AppSetting::set('global_chat_max_messages', $validated['max_messages'], 'integer'); + AppSetting::set('global_chat_max_length', $validated['max_length'], 'integer'); + AppSetting::set('global_chat_profanity_filter_enabled', $validated['profanity_filter_enabled'], 'boolean'); + AppSetting::set('global_chat_banned_words', $validated['banned_words'] ?? '', 'string'); + + // Immediately prune if current count exceeds new limit + ChatMessage::pruneOldMessages($validated['max_messages']); + + // Broadcast settings update to global-chat channel + try { + broadcast(new \App\Events\ChatSettingsUpdated()); + } catch (\Throwable $e) { + // Log without failing response + } return back()->with('success', 'Global chat settings updated successfully.'); } @@ -44,4 +114,22 @@ public function clearMessages() return back()->with('success', 'All chat messages have been cleared.'); } + + public function updateReportStatus(Request $request, ChatReport $report) + { + $validated = $request->validate([ + 'status' => ['required', 'string', 'in:pending,reviewed,dismissed'], + ]); + + $report->update(['status' => $validated['status']]); + + return back()->with('success', 'Report status updated.'); + } + + public function deleteReport(ChatReport $report) + { + $report->delete(); + + return back()->with('success', 'Report deleted successfully.'); + } } diff --git a/app/Http/Controllers/Admin/DashboardController.php b/app/Http/Controllers/Admin/DashboardController.php index a941f73..0d19664 100644 --- a/app/Http/Controllers/Admin/DashboardController.php +++ b/app/Http/Controllers/Admin/DashboardController.php @@ -3,7 +3,6 @@ namespace App\Http\Controllers\Admin; use App\Http\Controllers\Controller; -use App\Models\AppSetting; use Illuminate\Http\Request; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Http; @@ -14,12 +13,7 @@ class DashboardController extends Controller { public function index() { - return Inertia::render('admin/Dashboard', [ - 'chatSettings' => [ - 'enabled' => AppSetting::get('global_chat_enabled', true), - 'audience' => AppSetting::get('global_chat_audience', 'verified_members'), - ], - ]); + return Inertia::render('admin/Dashboard'); } public function analytics(Request $request) diff --git a/app/Http/Controllers/ChatController.php b/app/Http/Controllers/ChatController.php index 58f8829..cfc14ea 100644 --- a/app/Http/Controllers/ChatController.php +++ b/app/Http/Controllers/ChatController.php @@ -6,8 +6,10 @@ use App\Events\ChatMessageSent; use App\Models\AppSetting; use App\Models\ChatMessage; +use App\Services\ChatProfanityFilter; use Illuminate\Http\Request; use Illuminate\Support\Facades\RateLimiter; +use Illuminate\Support\Str; use Inertia\Inertia; class ChatController extends Controller @@ -20,15 +22,21 @@ public function index(Request $request) $isEnabled = AppSetting::get('global_chat_enabled', true); $audience = AppSetting::get('global_chat_audience', 'verified_members'); // 'verified_members', 'all', 'disabled' $cooldownSeconds = (int) AppSetting::get('global_chat_cooldown_seconds', 30); + $maxMessages = (int) AppSetting::get('global_chat_max_messages', 200); + $maxLength = (int) AppSetting::get('global_chat_max_length', 280); // Determine if user can post $canPost = false; $reason = null; if (! $isEnabled || $audience === 'disabled') { - $reason = 'Global chat is currently disabled for maintenance.'; + $customReason = trim((string) AppSetting::get('global_chat_disabled_reason', '')); + $reason = ! empty($customReason) ? $customReason : 'Global chat is currently disabled for maintenance.'; } elseif (! $user) { $reason = 'Please sign in to join the conversation.'; + } elseif ($user->isChatBanned()) { + $bannedUntilFormatted = $user->chat_banned_until->diffForHumans(); + $reason = "You are temporarily banned from chat until {$user->chat_banned_until->toDateTimeString()} ({$bannedUntilFormatted})."; } elseif ($audience === 'verified_members') { if ($user->is_verified || $user->can('view admin')) { $canPost = true; @@ -39,16 +47,18 @@ public function index(Request $request) $canPost = true; } - // Fetch last 200 messages in chronological order + // Fetch last X messages in chronological order $messages = ChatMessage::with(['user:id,name,username,image_path,institution', 'user.roles:id,name']) ->latest('id') - ->take(200) + ->take($maxMessages) ->get() ->reverse() ->values() ->map(fn (ChatMessage $msg) => [ 'id' => $msg->id, 'content' => $msg->content, + 'reply_to_id' => $msg->reply_to_id, + 'reply_to_content' => $msg->reply_to_content, 'created_at' => $msg->created_at->toIso8601String(), 'user' => [ 'id' => $msg->user->id, @@ -67,6 +77,8 @@ public function index(Request $request) 'enabled' => (bool) $isEnabled, 'audience' => $audience, 'cooldown_seconds' => $cooldownSeconds, + 'max_messages' => $maxMessages, + 'max_length' => $maxLength, 'can_post' => $canPost, 'reason' => $reason, 'can_delete' => (bool) $user?->can('manage chat'), @@ -81,6 +93,8 @@ public function index(Request $request) 'enabled' => (bool) $isEnabled, 'audience' => $audience, 'cooldown_seconds' => $cooldownSeconds, + 'max_messages' => $maxMessages, + 'max_length' => $maxLength, 'can_post' => $canPost, 'reason' => $reason, 'can_delete' => (bool) $user?->can('manage chat'), @@ -98,10 +112,24 @@ public function store(Request $request) $isEnabled = AppSetting::get('global_chat_enabled', true); $audience = AppSetting::get('global_chat_audience', 'verified_members'); $cooldownSeconds = (int) AppSetting::get('global_chat_cooldown_seconds', 30); + $maxMessages = (int) AppSetting::get('global_chat_max_messages', 200); + $maxLength = (int) AppSetting::get('global_chat_max_length', 280); // Check if chat is enabled if (! $isEnabled || $audience === 'disabled') { - return response()->json(['message' => 'Global chat is currently disabled.'], 403); + $customReason = trim((string) AppSetting::get('global_chat_disabled_reason', '')); + $msg = ! empty($customReason) ? $customReason : 'Global chat is currently disabled.'; + + return response()->json(['message' => $msg], 403); + } + + // Check if user is chat banned + if ($user->isChatBanned()) { + $bannedUntilFormatted = $user->chat_banned_until->diffForHumans(); + + return response()->json([ + 'message' => "You are banned from sending messages until {$user->chat_banned_until->toDateTimeString()} ({$bannedUntilFormatted}).", + ], 403); } // Check audience permission @@ -121,14 +149,47 @@ public function store(Request $request) } $validated = $request->validate([ - 'content' => ['required', 'string', 'max:280'], + 'content' => ['required', 'string', "max:{$maxLength}"], + 'reply_to_id' => ['nullable', 'integer'], ]); $content = trim($validated['content']); + // Check for abusive / prohibited language + if (ChatProfanityFilter::hasProfanity($content)) { + return response()->json([ + 'message' => 'Your message contains inappropriate or prohibited language. If you try again, you may be temporarily banned from chat.', + ], 422); + } + + // Prevent duplicate message sent twice in a streak by the same user + $lastMessage = ChatMessage::where('user_id', $user->id) + ->latest('id') + ->first(); + + if ($lastMessage && mb_strtolower($lastMessage->content) === mb_strtolower($content)) { + return response()->json([ + 'message' => 'You cannot send the exact same message twice in a row.', + ], 422); + } + + $replyToId = $validated['reply_to_id'] ?? null; + $replyToContent = null; + + if ($replyToId) { + $parentMessage = ChatMessage::find($replyToId); + if ($parentMessage) { + $replyToContent = Str::limit($parentMessage->content, 97, '...'); + } else { + $replyToId = null; + } + } + $message = ChatMessage::create([ 'user_id' => $user->id, 'content' => $content, + 'reply_to_id' => $replyToId, + 'reply_to_content' => $replyToContent, ]); // Record rate limit for configured cooldown seconds @@ -136,8 +197,8 @@ public function store(Request $request) RateLimiter::hit($rateLimitKey, $cooldownSeconds); } - // Keep rolling buffer of latest 200 messages in DB - ChatMessage::pruneOldMessages(200); + // Keep rolling buffer of latest X messages in DB + ChatMessage::pruneOldMessages($maxMessages); // Broadcast to Pusher Channels try { @@ -151,6 +212,8 @@ public function store(Request $request) return response()->json([ 'id' => $message->id, 'content' => $message->content, + 'reply_to_id' => $message->reply_to_id, + 'reply_to_content' => $message->reply_to_content, 'created_at' => $message->created_at->toIso8601String(), 'user' => [ 'id' => $message->user->id, @@ -169,8 +232,8 @@ public function destroy(Request $request, ChatMessage $message) $user = $request->user(); abort_unless($user, 401); - // Can delete if own message or has manage chat permission - if ($message->user_id !== $user->id && ! $user->can('manage chat')) { + // Only staff with manage chat permission can delete messages + if (! $user->can('manage chat')) { abort(403, 'Unauthorized'); } @@ -185,4 +248,63 @@ public function destroy(Request $request, ChatMessage $message) return response()->json(['success' => true]); } + + public function report(Request $request) + { + $user = $request->user(); + abort_unless($user, 401, 'Unauthenticated'); + + $validated = $request->validate([ + 'reported_user_id' => ['nullable', 'integer', 'exists:users,id'], + 'reported_user_name' => ['nullable', 'string', 'max:255'], + 'reported_user_username' => ['nullable', 'string', 'max:255'], + 'message_content' => ['required', 'string', 'max:1000'], + 'message_sent_at' => ['nullable', 'date'], + 'reason' => ['nullable', 'string', 'max:255'], + ]); + + // Prevent duplicate reporting of the exact same message content by the same user + $alreadyReported = \App\Models\ChatReport::where('reporter_id', $user->id) + ->where('message_content', $validated['message_content']) + ->where('reported_user_id', $validated['reported_user_id'] ?? null) + ->exists(); + + if ($alreadyReported) { + return response()->json([ + 'message' => 'You have already reported this message.', + ], 422); + } + + $report = \App\Models\ChatReport::create([ + 'reporter_id' => $user->id, + 'reported_user_id' => $validated['reported_user_id'] ?? null, + 'reported_user_name' => $validated['reported_user_name'] ?? null, + 'reported_user_username' => $validated['reported_user_username'] ?? null, + 'message_content' => $validated['message_content'], + 'message_sent_at' => $validated['message_sent_at'] ?? null, + 'reason' => $validated['reason'] ?? 'Inappropriate message', + 'status' => 'pending', + ]); + + // Auto-ban logic: If the reported user has 5 or more reports on this message/content, auto-ban for 1 day + if (! empty($validated['reported_user_id'])) { + $reportedUser = \App\Models\User::find($validated['reported_user_id']); + if ($reportedUser && ! $reportedUser->can('view admin')) { + $totalReportsForMessage = \App\Models\ChatReport::where('reported_user_id', $reportedUser->id) + ->where('message_content', $validated['message_content']) + ->count(); + + if ($totalReportsForMessage >= 5) { + $reportedUser->update([ + 'chat_banned_until' => now()->addDay(), + ]); + } + } + } + + return response()->json([ + 'message' => 'Message reported successfully. Our team will review it.', + 'report_id' => $report->id, + ], 201); + } } diff --git a/app/Http/Requests/User/UpdateUserRequest.php b/app/Http/Requests/User/UpdateUserRequest.php index 84638ae..71ff13d 100644 --- a/app/Http/Requests/User/UpdateUserRequest.php +++ b/app/Http/Requests/User/UpdateUserRequest.php @@ -37,6 +37,7 @@ public function rules(): array 'role' => ['sometimes', 'string'], 'permissions' => ['sometimes', 'array'], 'permissions.*' => ['string', 'exists:permissions,name'], + 'chat_banned_until' => ['sometimes', 'nullable', 'date'], ]; return $rules; diff --git a/app/Models/ChatMessage.php b/app/Models/ChatMessage.php index 9f2f71f..9f87c1d 100644 --- a/app/Models/ChatMessage.php +++ b/app/Models/ChatMessage.php @@ -10,6 +10,8 @@ class ChatMessage extends Model protected $fillable = [ 'user_id', 'content', + 'reply_to_id', + 'reply_to_content', ]; public function user(): BelongsTo diff --git a/app/Models/ChatReport.php b/app/Models/ChatReport.php new file mode 100644 index 0000000..7126f13 --- /dev/null +++ b/app/Models/ChatReport.php @@ -0,0 +1,40 @@ + 'datetime', + ]; + } + + public function reporter(): BelongsTo + { + return $this->belongsTo(User::class, 'reporter_id'); + } + + public function reportedUser(): BelongsTo + { + return $this->belongsTo(User::class, 'reported_user_id'); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index c86e494..03f0191 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -43,6 +43,7 @@ class User extends Authenticatable 'receive_emails', 'google_id', 'email_verified_at', + 'chat_banned_until', 'image_path', 'about', 'title', @@ -74,6 +75,11 @@ public function getIsVerifiedAttribute(): bool : $this->roles()->exists(); } + public function isChatBanned(): bool + { + return $this->chat_banned_until !== null && $this->chat_banned_until->isFuture(); + } + public function getImageUrlAttribute() { if (! $this->image_path) { @@ -87,6 +93,7 @@ protected function casts(): array { return [ 'email_verified_at' => 'datetime', + 'chat_banned_until' => 'datetime', 'password' => 'hashed', 'receive_emails' => 'boolean', ]; diff --git a/app/Services/ChatProfanityFilter.php b/app/Services/ChatProfanityFilter.php new file mode 100644 index 0000000..f93d054 --- /dev/null +++ b/app/Services/ChatProfanityFilter.php @@ -0,0 +1,103 @@ + 'a', + '4' => 'a', + '$' => 's', + '5' => 's', + '1' => 'i', + '!' => 'i', + '|' => 'i', + '0' => 'o', + '3' => 'e', + '8' => 'b', + '+' => 't', + '7' => 't', + ]; + + $text = strtr($text, $substitutions); + + // Collapse repeated characters: e.g. "fuuuck" -> "fuck", "biiiitch" -> "bitch" + $text = preg_replace('/(.)\1{2,}/u', '$1', $text); + + return $text; + } + + /** + * Get the active list of banned words from AppSetting. + * + * @return array + */ + public static function getBannedWords(): array + { + $customWords = AppSetting::get('global_chat_banned_words', ''); + + if (empty($customWords)) { + return []; + } + + if (is_array($customWords)) { + return $customWords; + } + + // Split by commas or newlines + $words = preg_split('/[\r\n,]+/', (string) $customWords); + + return array_values(array_filter(array_map('trim', $words), fn ($w) => $w !== '')); + } +} diff --git a/database/migrations/2026_08_27_160307_add_chat_banned_until_to_users_table.php b/database/migrations/2026_08_27_160307_add_chat_banned_until_to_users_table.php new file mode 100644 index 0000000..90df143 --- /dev/null +++ b/database/migrations/2026_08_27_160307_add_chat_banned_until_to_users_table.php @@ -0,0 +1,28 @@ +timestamp('chat_banned_until')->nullable()->after('receive_emails'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('chat_banned_until'); + }); + } +}; diff --git a/database/migrations/2026_08_27_161100_create_chat_reports_table.php b/database/migrations/2026_08_27_161100_create_chat_reports_table.php new file mode 100644 index 0000000..c3ca716 --- /dev/null +++ b/database/migrations/2026_08_27_161100_create_chat_reports_table.php @@ -0,0 +1,38 @@ +id(); + $table->foreignId('reporter_id')->nullable()->constrained('users')->nullOnDelete(); + $table->foreignId('reported_user_id')->nullable()->constrained('users')->nullOnDelete(); + $table->string('reported_user_name')->nullable(); + $table->string('reported_user_username')->nullable(); + $table->text('message_content'); + $table->timestamp('message_sent_at')->nullable(); + $table->string('reason', 255)->nullable(); + $table->string('status', 50)->default('pending'); // pending, reviewed, dismissed + $table->timestamps(); + + $table->index('status'); + $table->index('created_at'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('chat_reports'); + } +}; diff --git a/database/migrations/2026_08_27_163500_add_reply_fields_to_chat_messages_table.php b/database/migrations/2026_08_27_163500_add_reply_fields_to_chat_messages_table.php new file mode 100644 index 0000000..43a6481 --- /dev/null +++ b/database/migrations/2026_08_27_163500_add_reply_fields_to_chat_messages_table.php @@ -0,0 +1,29 @@ +unsignedBigInteger('reply_to_id')->nullable()->after('user_id'); + $table->string('reply_to_content', 100)->nullable()->after('reply_to_id'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('chat_messages', function (Blueprint $table) { + $table->dropColumn(['reply_to_id', 'reply_to_content']); + }); + } +}; diff --git a/resources/js/components/admin/UserRow.vue b/resources/js/components/admin/UserRow.vue index 26175ca..8ac1d8c 100644 --- a/resources/js/components/admin/UserRow.vue +++ b/resources/js/components/admin/UserRow.vue @@ -1,17 +1,22 @@ @@ -315,18 +563,34 @@ onUnmounted(() => {
- -
-

- Global Chat -

-

+

+
+

+ Global Chat +

+

+ অন্যান্য শিক্ষার্থীদের সাথে সরাসরি কথা বলুন ও প্রশ্ন শেয়ার + করুন। +

+
+ + +
@@ -371,18 +635,22 @@ onUnmounted(() => {
+
{{ formatTime(msg.created_at) }} - + + + + + + + + + + + +
+ +
+ + + "{{ msg.reply_to_content }}" + +
+

{

+ +
+
+ + + Replying to + {{ + activeReplyTo.user.name + }}: + "{{ activeReplyTo.content }}" + +
+ +
+
{ >
- {{ 280 - inputContent.length }} + {{ maxLengthLimit - inputContent.length }}
@@ -568,5 +930,308 @@ onUnmounted(() => {
+ + +
+
+
+
+
+ +
+

+ Report Message +

+
+ +
+ + +
+
+ +
+

+ {{ reportSuccessMessage }} +

+
+ + + + +
+ {{ reportErrorMessage }} +
+ + +
+
+ + {{ reportingMessage.user.name }} (@{{ + reportingMessage.user.username + }}) + + {{ + formatTime(reportingMessage.created_at) + }} +
+

+ "{{ reportingMessage.content }}" +

+
+ + +
+ + +
+ + +
+ + +
+ +
+
+ + +
+ +
+ + +
+ +
+
+
+ +
+
+

+ Global Chat Rules & Guidelines +

+

+ সবার জন্য চ্যাট নিরাপদ ও ফ্রেন্ডলি রাখতে নিচের + নিয়মগুলো মেনে চলুন। +

+
+
+ +
+ + +
+
+
+ 1 +
+
+ পরস্পরকে সম্মান করুন (Respectful + Environment): +

+ অন্য শিক্ষার্থী ও মডারেটরদের সাথে শালীন আচরণ + বজায় রাখুন। কোনো ধরনের ব্যক্তিগত আক্রমণ, বুলিং + বা হেট স্পিচ কঠোরভাবে নিষিদ্ধ। +

+
+
+ +
+
+ 2 +
+
+ খারাপ ভাষা ও গালিগালাজ নিষেধ (No Abuse / + Slang): +

+ বাংলা, ইংরেজি বা বাংলিশ কোনো ভাষাতেই গালাগালি বা + অশালীন শব্দ ব্যবহার করা যাবে না। এমন মেসেজ + অটোমেটিক ব্লক হবে। +

+
+
+ +
+
+ 3 +
+
+ স্প্যামিং ও অ্যাডভার্টাইজিং নিষেধ (No + Spam): +

+ একই মেসেজ বারবার পাঠানো, চ্যাট ফ্লাড করা বা + অনুমতি ছাড়া কোনো প্রোমোশন বা অপ্রাসঙ্গিক লিংক + শেয়ার করা যাবে না। +

+
+
+ +
+
+ 4 +
+
+ অনুপযুক্ত মেসেজ রিপোর্ট করুন (Report + Violations): +

+ কারো মেসেজে নিয়ম লঙ্ঘন দেখতে পেলে মেসেজের + ডানপাশে থাকা ফ্ল্যাগ/রিপোর্ট বাটনে ক্লিক করে + মডারেটরদের জানান। +

+
+
+
+ + +
+
+ + অটো-ব্যান ও এনফোর্সমেন্ট পলিসি: +
+

+ একটি মেসেজে ৫ জন শিক্ষার্থীর রিপোর্ট (৫ Reports) পড়লে + সংশ্লিষ্ট ব্যবহারকারী + স্বয়ংক্রিয়ভাবে ১ দিনের জন্য চ্যাট ব্যান + হবেন। এছাড়া নিয়ম ভঙ্গে মডারেটররা তাৎক্ষণিক স্থায়ী ব্যান + দিতে পারেন। +

+
+ + +
+ +
+
+
diff --git a/resources/js/pages/admin/ChatSettings.vue b/resources/js/pages/admin/ChatSettings.vue index 1d45157..35e73e7 100644 --- a/resources/js/pages/admin/ChatSettings.vue +++ b/resources/js/pages/admin/ChatSettings.vue @@ -1,5 +1,5 @@