diff --git a/app/Console/Commands/Nostr/PublishUnpublishedItems.php b/app/Console/Commands/Nostr/PublishUnpublishedItems.php index 05e5e61..1c6e0e5 100644 --- a/app/Console/Commands/Nostr/PublishUnpublishedItems.php +++ b/app/Console/Commands/Nostr/PublishUnpublishedItems.php @@ -2,46 +2,98 @@ namespace App\Console\Commands\Nostr; +use App\Models\Course; +use App\Models\CourseEvent; +use App\Models\Meetup; +use App\Models\MeetupEvent; use App\Traits\NostrTrait; use Illuminate\Console\Command; +use Illuminate\Database\Eloquent\Model; class PublishUnpublishedItems extends Command { use NostrTrait; - /** - * The name and signature of the console command. - * @var string - */ protected $signature = 'nostr:publish {--model=}'; + protected $description = 'Publish unpublished items to Nostr'; - /** - * The console command description. - * @var string - */ - protected $description = 'Command description'; + private const TZ_MAP = [ + 'de' => 'Europe/Berlin', + 'nl' => 'Europe/Amsterdam', + 'hu' => 'Europe/Budapest', + 'pl' => 'Europe/Warsaw', + 'es' => 'Europe/Madrid', + 'pt' => 'Europe/Lisbon', + ]; - /** - * Execute the console command. - */ public function handle(): void { - config(['app.user-timezone' => 'Europe/Berlin']); $modelName = $this->option('model'); - $className = '\\App\Models\\' . $modelName; - $model = $className::query() - ->whereNull('nostr_status') - ->when($modelName === 'BitcoinEvent', fn($q) => $q->where('from', '>', now())) - ->when($modelName === 'CourseEvent', fn($q) => $q->where('from', '>', now())) - ->when($modelName === 'MeetupEvent', fn($q) => $q->where('start', '>', now())) - ->when($modelName === 'LibraryItem', fn($q) => $q - ->where('type', '<>', 'markdown_article') - ->where('type', '<>', 'bindle') - ) - ->orderByDesc('created_at') - ->first(); - if ($model) { - $this->publishOnNostr($model, $this->getText($model)); + $modelClass = '\\App\\Models\\'.$modelName; + + // Define query logic per model type + $query = match ($modelName) { + 'Course' => $modelClass::whereNull('nostr_status')->orderByDesc('created_at'), + 'CourseEvent' => $modelClass::whereNull('nostr_status') + ->where('from', '>', now()) + ->orderByDesc('created_at'), + 'Meetup' => $modelClass::with('city.country') + ->whereNull('nostr_status') + ->orderByDesc('created_at'), + 'MeetupEvent' => $modelClass::with('meetup.city.country') + ->whereNull('nostr_status') + ->where('start', '>', now()) + ->orderByDesc('created_at'), + default => null, + }; + + if (!$query) { + $this->error("Unsupported model: {$modelName}"); + return; + } + + $model = $query->first(); + + if (!$model) { + $this->info("No unpublished items for model: {$modelName}"); + return; + } + + // Get country code and configure timezone/locale if applicable + $countryCode = $this->getCountryCode($model); + $this->configureForCountry($countryCode); + + $text = $this->getText($model, $countryCode); + if ($text) { + $result = $this->publishOnNostr($model, $text); + if ($result['success']) { + $this->info("Published successfully for {$modelName}"); + } else { + $this->error("Failed to publish for {$modelName}: ".$result['errorOutput']); + } + } else { + $this->error("No text generated for {$modelName}"); } } + + private function getCountryCode(Model $model): string + { + return match (true) { + $model instanceof Meetup => $model->city?->country?->code ?? 'de', + $model instanceof MeetupEvent => $model->meetup?->city?->country?->code ?? 'de', + $model instanceof Course => $model->lecturer?->country?->code ?? 'de', + $model instanceof CourseEvent => $model->course?->lecturer?->country?->code ?? 'de', + default => 'de', // Default fallback + }; + } + + private function configureForCountry(string $countryCode): void + { + // Set user timezone and locale based on country code + $timezone = self::TZ_MAP[$countryCode] ?? 'UTC'; + config([ + 'app.user-timezone' => $timezone, + 'app.locale' => $countryCode, + ]); + } } diff --git a/app/Http/Middleware/DomainMiddleware.php b/app/Http/Middleware/DomainMiddleware.php index 4aacc5f..b3b1ec7 100644 --- a/app/Http/Middleware/DomainMiddleware.php +++ b/app/Http/Middleware/DomainMiddleware.php @@ -30,11 +30,17 @@ class DomainMiddleware 'lang_country' => 'pl-PL', 'app_name' => 'DWADZIEŚCIA JEDEN Portal', ], + 'pl.localhost' => [ 'locale' => 'pl', 'lang_country' => 'pl-PL', 'app_name' => 'DWADZIEŚCIA JEDEN Portal', ], + 'hu.localhost' => [ + 'locale' => 'hu', + 'lang_country' => 'hu-HU', + 'app_name' => 'HUSZONEGY Portál', + ], ]; // App-Locale dynamisch setzen diff --git a/app/Models/Meetup.php b/app/Models/Meetup.php index 157c9b3..1324767 100644 --- a/app/Models/Meetup.php +++ b/app/Models/Meetup.php @@ -76,7 +76,7 @@ class Meetup extends Model implements HasMedia $this ->addMediaCollection('logo') ->singleFile() - ->useFallbackUrl(asset('img/einundzwanzig.png')); + ->useFallbackUrl(get_domain_image()); } public function createdBy(): BelongsTo diff --git a/app/Traits/NostrTrait.php b/app/Traits/NostrTrait.php index 52959e0..4f19831 100644 --- a/app/Traits/NostrTrait.php +++ b/app/Traits/NostrTrait.php @@ -6,11 +6,12 @@ use App\Models\Course; use App\Models\CourseEvent; use App\Models\Meetup; use App\Models\MeetupEvent; +use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\Process; trait NostrTrait { - public function publishOnNostr($model, $text): array + public function publishOnNostr(Model $model, string $text): array { if (app()->environment('local')) { return [ @@ -21,12 +22,12 @@ trait NostrTrait ]; } - //noscl publish "Good morning!" + // Use array to pass arguments safely and avoid shell injection $result = Process::timeout(60 * 5) - ->run('noscl publish "'.$text.'"'); + ->run(['noscl', 'publish', $text]); if ($result->successful()) { - $model->nostr_status = $result->output(); + $model->nostr_status = trim($result->output()); $model->save(); } @@ -38,67 +39,63 @@ trait NostrTrait ]; } - public function getText($model) + public function getText(Model $model, string $countryCode): string|null { - $from = ''; - if ($model instanceof CourseEvent) { - if ($model->course->lecturer->nostr) { - $from .= '@'.$model->course->lecturer->nostr; - } else { - $from .= $model->course->lecturer->name; - } + return match (true) { + $model instanceof CourseEvent => __('nostr.course_event_text', [ + 'from' => $this->getFrom($model), + 'name' => $model->course->name, + 'description' => str($model->course->description)->toString(), + 'url' => $this->getUrl($model, $countryCode), + ]), + $model instanceof MeetupEvent => __('nostr.meetup_event_text', [ + 'from' => $this->getFrom($model), + 'start' => $model->start->asDateTime(), + 'location' => $model->location, + 'url' => $this->getUrl($model, $countryCode), + ]), + $model instanceof Meetup => __('nostr.meetup_text', [ + 'from' => $this->getFrom($model), + 'url' => $this->getUrl($model, $countryCode), + ]), + $model instanceof Course => __('nostr.course_text', [ + 'from' => $this->getFrom($model), + 'name' => $model->name, + 'description' => str($model->description)->toString(), + 'url' => $this->getUrl($model, $countryCode), + ]), + default => null, + }; + } - return sprintf("Unser Dozent %s hat einen neuen Kurs-Termin eingestellt:\n%s\n%s\n%s\n\n#Bitcoin #Kurs #Education #Einundzwanzig #gesundesgeld #einundzwanzig_portal_lecturer_%s", - $from, - $model->course->name, - str($model->course->description)->toString(), - url()->route('courses.landingpage', - ['country' => str(session('lang_country', 'de'))->after('-')->lower(), 'course' => $model->course]), - str($model->course->lecturer->slug)->replace('-', '_'), - ); - } - if ($model instanceof MeetupEvent) { - $from = $model->meetup->name; - if ($model->meetup->nostr) { - $from .= ' @'.$model->meetup->nostr; - } - - return sprintf("%s hat einen neuen Termin eingestellt:\n%s\n%s\n%s\n\n#Bitcoin #Meetup #Einundzwanzig #gesundesgeld #einundzwanzig_portal_%s", - $from, - $model->start->asDateTime(), - $model->location, - url()->route('meetups.landingpage-event', - ['country' => str(session('lang_country', 'de'))->after('-')->lower(), 'meetup' => $model->meetup, 'event' => $model]), - str($model->meetup->slug)->replace('-', '_'), - ); - } - if ($model instanceof Meetup) { - $from = $model->name; - if ($model->nostr) { - $from .= ' @'.$model->nostr; - } - - return sprintf("Eine neue Meetup Gruppe wurde hinzugefügt:\n%s\n%s\n\n#Bitcoin #Meetup #Einundzwanzig #gesundesgeld #einundzwanzig_portal_%s", - $from, - url()->route('meetups.landingpage', ['country' => $model->city->country->code, 'meetup' => $model]), - str($model->slug)->replace('-', '_'), - ); - } + private function getFrom(Model $model): string + { if ($model instanceof Course) { - if ($model->lecturer->nostr) { - $from .= '@'.$model->lecturer->nostr; - } else { - $from .= $model->lecturer->name; - } - - return sprintf("Unser Dozent %s hat einen neuen Kurs eingestellt:\n%s\n%s\n%s\n\n#Bitcoin #Kurs #Education #Einundzwanzig #gesundesgeld #einundzwanzig_portal_lecturer_%s", - $from, - $model->name, - str($model->description)->toString(), - url()->route('courses.landingpage', - ['country' => str(session('lang_country', 'de'))->after('-')->lower(), 'course' => $model]), - str($model->lecturer->slug)->replace('-', '_'), - ); + return $model->lecturer->nostr ? '@'.$model->lecturer->nostr : $model->lecturer->name; + } elseif ($model instanceof CourseEvent) { + return $this->getFrom($model->course); + } elseif ($model instanceof Meetup) { + return $model->name.($model->nostr ? ' @'.$model->nostr : ''); + } elseif ($model instanceof MeetupEvent) { + return $this->getFrom($model->meetup); } + + return ''; + } + + private function getUrl(Model $model, string $countryCode): string + { + if ($model instanceof Course) { + return route('courses.landingpage', ['country' => $countryCode, 'course' => $model]); + } elseif ($model instanceof CourseEvent) { + return route('courses.landingpage', ['country' => $countryCode, 'course' => $model->course]); + } elseif ($model instanceof Meetup) { + return route('meetups.landingpage', ['country' => $countryCode, 'meetup' => $model]); + } elseif ($model instanceof MeetupEvent) { + return route('meetups.landingpage-event', + ['country' => $countryCode, 'meetup' => $model->meetup, 'event' => $model]); + } + + return ''; } } diff --git a/lang/de.json b/lang/de.json index 771c7d1..78b428d 100644 --- a/lang/de.json +++ b/lang/de.json @@ -547,5 +547,11 @@ "Service löschen?": "", "Möchten Sie den Service wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden.": "", "Mindestens eine URL oder IP muss angegeben werden.": "", - "IP Adresse": "" + "IP Adresse": "", + "nostr.course_event_text": "Unser Dozent :from hat einen neuen Kurs-Termin eingestellt:\n:name\n:description\n:url\n\n#Bitcoin #Einundzwanzig #gesundesgeld", + "nostr.meetup_event_text": ":from hat einen neuen Termin eingestellt:\n:start\n:location\n:url\n\n#Bitcoin #Meetup #Einundzwanzig", + "nostr.meetup_text": "Eine neue Meetup Gruppe wurde hinzugefügt:\n:from\n:url\n\n#Bitcoin #Meetup #Einundzwanzig", + "nostr.course_text": "Unser Dozent :from hat einen neuen Kurs eingestellt:\n:name\n:description\n:url\n\n#Bitcoin #Einundzwanzig", + "Einundzwanzig Community": "", + "Allgemeine Bitcoin Community": "" } \ No newline at end of file diff --git a/lang/en.json b/lang/en.json index 3943803..d0ff171 100644 --- a/lang/en.json +++ b/lang/en.json @@ -547,5 +547,11 @@ "Service löschen?": "Delete service?", "Möchten Sie den Service wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden.": "Do you really want to delete the service? This action cannot be undone.", "Mindestens eine URL oder IP muss angegeben werden.": "At least one URL or IP must be provided.", - "IP Adresse": "IP address" -} + "IP Adresse": "IP address", + "nostr.course_event_text": "Our lecturer :from has scheduled a new course event:\n:name\n:description\n:url", + "nostr.meetup_event_text": ":from has scheduled a new event:\n:start\n:location\n:url", + "nostr.meetup_text": "A new Meetup group has been added:\n:from\n:url", + "nostr.course_text": "Our lecturer :from has added a new course:\n:name\n:description\n:url", + "Einundzwanzig Community": "Twenty One Community", + "Allgemeine Bitcoin Community": "General Bitcoin Community" +} \ No newline at end of file diff --git a/lang/es.json b/lang/es.json index f13d640..d38312e 100644 --- a/lang/es.json +++ b/lang/es.json @@ -546,5 +546,11 @@ "Service löschen?": "¿Eliminar servicio?", "Möchten Sie den Service wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden.": "¿Realmente desea eliminar el servicio? Esta acción no se puede deshacer.", "Mindestens eine URL oder IP muss angegeben werden.": "Se debe especificar al menos una URL o IP.", - "IP Adresse": "Dirección IP" -} + "IP Adresse": "Dirección IP", + "nostr.course_event_text": "Nuestro profesor :from ha programado un nuevo evento de curso:\n:name\n:description\n:url", + "nostr.meetup_event_text": ":from ha programado un nuevo evento:\n:start\n:location\n:url", + "nostr.meetup_text": "Se ha añadido un nuevo grupo Meetup:\n:from\n:url", + "nostr.course_text": "Nuestro profesor :from ha añadido un nuevo curso:\n:name\n:description\n:url", + "Einundzwanzig Community": "Comunidad Veintiuno", + "Allgemeine Bitcoin Community": "Comunidad General de Bitcoin" +} \ No newline at end of file diff --git a/lang/hu.json b/lang/hu.json index ccff889..1d09f92 100644 --- a/lang/hu.json +++ b/lang/hu.json @@ -542,5 +542,11 @@ "Service löschen?": "Szolgáltatás törlése?", "Möchten Sie den Service wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden.": "Biztosan törölni szeretné a szolgáltatást? Ez a művelet nem vonható vissza.", "Mindestens eine URL oder IP muss angegeben werden.": "Legalább egy URL-t vagy IP-t meg kell adni.", - "IP Adresse": "IP cím" -} + "IP Adresse": "IP cím", + "nostr.course_event_text": "Szakemberünk :from új kurzus eseményt tervezett:\n:name\n:description\n:url", + "nostr.meetup_event_text": ":from új eseményt tervezett:\n:start\n:location\n:url", + "nostr.meetup_text": "Új Meetup csoport került hozzáadásra:\n:from\n:url", + "nostr.course_text": "Szakemberünk :from új kurzust adott hozzá:\n:name\n:description\n:url", + "Einundzwanzig Community": "Huszonegy Közösség", + "Allgemeine Bitcoin Community": "Általános Bitcoin Közösség" +} \ No newline at end of file diff --git a/lang/nl.json b/lang/nl.json index f7c51d9..0824ae1 100644 --- a/lang/nl.json +++ b/lang/nl.json @@ -544,5 +544,11 @@ "Service löschen?": "Service verwijderen?", "Möchten Sie den Service wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden.": "Wilt u de service echt verwijderen? Deze actie kan niet ongedaan worden gemaakt.", "Mindestens eine URL oder IP muss angegeben werden.": "Er moet minimaal één URL of IP worden opgegeven.", - "IP Adresse": "IP-adres" -} + "IP Adresse": "IP-adres", + "nostr.course_event_text": "Onze docent :from heeft een nieuw cursusevent gepland:\n:name\n:description\n:url", + "nostr.meetup_event_text": ":from heeft een nieuw evenement gepland:\n:start\n:location\n:url", + "nostr.meetup_text": "Er is een nieuwe Meetup-groep toegevoegd:\n:from\n:url", + "nostr.course_text": "Onze docent :from heeft een nieuwe cursus toegevoegd:\n:name\n:description\n:url", + "Einundzwanzig Community": "Eenentwintig Community", + "Allgemeine Bitcoin Community": "Algemene Bitcoin Gemeenschap" +} \ No newline at end of file diff --git a/lang/pl.json b/lang/pl.json index ce749dd..ef437ff 100644 --- a/lang/pl.json +++ b/lang/pl.json @@ -540,5 +540,11 @@ "Service löschen?": "Usunąć usługę?", "Möchten Sie den Service wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden.": "Czy na pewno chcesz usunąć tę usługę? Ta akcja nie może zostać cofnięta.", "Mindestens eine URL oder IP muss angegeben werden.": "Należy podać co najmniej jeden URL lub adres IP.", - "IP Adresse": "Adres IP" -} + "IP Adresse": "Adres IP", + "nostr.course_event_text": "Nasz wykładowca :from zaplanował nowe wydarzenie kursu:\n:name\n:description\n:url", + "nostr.meetup_event_text": ":from zaplanował nowe wydarzenie:\n:start\n:location\n:url", + "nostr.meetup_text": "Nowa grupa Meetup została dodana:\n:from\n:url", + "nostr.course_text": "Nasz wykładowca :from dodał nowy kurs:\n:name\n:description\n:url", + "Einundzwanzig Community": "Społeczność Einundzwanzig", + "Allgemeine Bitcoin Community": "Ogólna społeczność Bitcoin" +} \ No newline at end of file diff --git a/lang/pt.json b/lang/pt.json index 7eb1b4d..9eb53e0 100644 --- a/lang/pt.json +++ b/lang/pt.json @@ -542,5 +542,11 @@ "Service löschen?": "Excluir serviço?", "Möchten Sie den Service wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden.": "Você realmente quer excluir o serviço? Esta ação não pode ser desfeita.", "Mindestens eine URL oder IP muss angegeben werden.": "Pelo menos uma URL ou IP deve ser fornecido.", - "IP Adresse": "Endereço IP" -} + "IP Adresse": "Endereço IP", + "nostr.course_event_text": "O nosso docente :from agendou um novo evento de curso:\n:name\n:description\n:url", + "nostr.meetup_event_text": ":from agendou um novo evento:\n:start\n:location\n:url", + "nostr.meetup_text": "Um novo grupo Meetup foi adicionado:\n:from\n:url", + "nostr.course_text": "O nosso docente :from adicionou um novo curso:\n:name\n:description\n:url", + "Einundzwanzig Community": "Comunidade Einundzwanzig", + "Allgemeine Bitcoin Community": "Comunidade geral de Bitcoin" +} \ No newline at end of file diff --git a/resources/views/livewire/meetups/create.blade.php b/resources/views/livewire/meetups/create.blade.php index 7a4d077..9e6c486 100644 --- a/resources/views/livewire/meetups/create.blade.php +++ b/resources/views/livewire/meetups/create.blade.php @@ -266,8 +266,8 @@ class extends Component { {{ __('Gemeinschaft') }} {{ __('Keine') }} - einundzwanzig - bitcoin + {{ __('Einundzwanzig Community') }} + {{ __('Allgemeine Bitcoin Community') }} {{ __('Gemeinschafts- oder Organisationsname') }}