Add slack support to Firefly III

This commit is contained in:
James Cole 2022-09-24 12:14:27 +02:00
parent 2945f5fcde
commit 45d7042405
No known key found for this signature in database
GPG Key ID: B49A324B7EAD6D80
10 changed files with 138 additions and 17 deletions

View File

@ -75,8 +75,9 @@ class HomeController extends Controller
foreach (config('firefly.admin_notifications') as $item) { foreach (config('firefly.admin_notifications') as $item) {
$notifications[$item] = FireflyConfig::get(sprintf('notification_%s', $item), true)->data; $notifications[$item] = FireflyConfig::get(sprintf('notification_%s', $item), true)->data;
} }
$slackUrl = FireflyConfig::get('slack_webhook_url', '')->data;
return view('admin.index', compact('title', 'mainTitleIcon', 'email', 'notifications')); return view('admin.index', compact('title', 'mainTitleIcon', 'email', 'notifications', 'slackUrl'));
} }
public function notifications(Request $request): RedirectResponse public function notifications(Request $request): RedirectResponse
@ -86,7 +87,14 @@ class HomeController extends Controller
if ($request->has(sprintf('notification_%s', $item))) { if ($request->has(sprintf('notification_%s', $item))) {
$value = true; $value = true;
} }
FireflyConfig::set(sprintf('notification_%s',$item), $value); FireflyConfig::set(sprintf('notification_%s', $item), $value);
}
$url = (string) $request->get('slackUrl');
if ('' === $url) {
FireflyConfig::delete('slack_webhook_url');
}
if (str_starts_with($url, 'https://hooks.slack.com/services/')) {
FireflyConfig::set('slack_webhook_url', $url);
} }
session()->flash('success', (string) trans('firefly.notification_settings_saved')); session()->flash('success', (string) trans('firefly.notification_settings_saved'));

View File

@ -101,6 +101,7 @@ class PreferencesController extends Controller
$languages = config('firefly.languages'); $languages = config('firefly.languages');
$locale = app('preferences')->get('locale', config('firefly.default_locale', 'equal'))->data; $locale = app('preferences')->get('locale', config('firefly.default_locale', 'equal'))->data;
$listPageSize = app('preferences')->get('listPageSize', 50)->data; $listPageSize = app('preferences')->get('listPageSize', 50)->data;
$slackUrl = app('preferences')->get('slack_webhook_url', '')->data;
$customFiscalYear = app('preferences')->get('customFiscalYear', 0)->data; $customFiscalYear = app('preferences')->get('customFiscalYear', 0)->data;
$fiscalYearStartStr = app('preferences')->get('fiscalYearStart', '01-01')->data; $fiscalYearStartStr = app('preferences')->get('fiscalYearStart', '01-01')->data;
$fiscalYearStart = date('Y') . '-' . $fiscalYearStartStr; $fiscalYearStart = date('Y') . '-' . $fiscalYearStartStr;
@ -139,6 +140,7 @@ class PreferencesController extends Controller
'frontPageAccounts', 'frontPageAccounts',
'languages', 'languages',
'notifications', 'notifications',
'slackUrl',
'locales', 'locales',
'locale', 'locale',
'tjOptionalFields', 'tjOptionalFields',
@ -173,12 +175,12 @@ class PreferencesController extends Controller
// extract notifications: // extract notifications:
$all = $request->all(); $all = $request->all();
foreach(config('firefly.available_notifications') as $option) { foreach (config('firefly.available_notifications') as $option) {
$key = sprintf('notification_%s', $option); $key = sprintf('notification_%s', $option);
if(array_key_exists($key, $all)) { if (array_key_exists($key, $all)) {
app('preferences')->set($key, true); app('preferences')->set($key, true);
} }
if(!array_key_exists($key, $all)) { if (!array_key_exists($key, $all)) {
app('preferences')->set($key, false); app('preferences')->set($key, false);
} }
} }
@ -190,6 +192,16 @@ class PreferencesController extends Controller
session()->forget('end'); session()->forget('end');
session()->forget('range'); session()->forget('range');
// slack URL:
$url = (string) $request->get('slackUrl');
if(str_starts_with($url, 'https://hooks.slack.com/services/')){
app('preferences')->set('slack_webhook_url', $url);
}
if('' === $url) {
app('preferences')->delete('slack_webhook_url');
}
// custom fiscal year // custom fiscal year
$customFiscalYear = 1 === (int) $request->get('customFiscalYear'); $customFiscalYear = 1 === (int) $request->get('customFiscalYear');
$string = strtotime((string) $request->get('fiscalYearStart')); $string = strtotime((string) $request->get('fiscalYearStart'));

View File

@ -25,6 +25,7 @@ namespace FireflyIII\Notifications\Admin;
use FireflyIII\Mail\AdminTestMail; use FireflyIII\Mail\AdminTestMail;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification; use Illuminate\Notifications\Notification;
/** /**
@ -54,7 +55,7 @@ class TestNotification extends Notification
*/ */
public function via($notifiable) public function via($notifiable)
{ {
return ['mail']; return ['mail', 'slack'];
} }
/** /**
@ -69,6 +70,16 @@ class TestNotification extends Notification
->markdown('emails.admin-test', ['email' => $this->address]) ->markdown('emails.admin-test', ['email' => $this->address])
->subject((string) trans('email.admin_test_subject')); ->subject((string) trans('email.admin_test_subject'));
} }
/**
* Get the Slack representation of the notification.
*
* @param mixed $notifiable
* @return SlackMessage
*/
public function toSlack($notifiable)
{
return (new SlackMessage)->content((string)trans('email.admin_test_subject'));
}
/** /**
* Get the array representation of the notification. * Get the array representation of the notification.

View File

@ -25,8 +25,12 @@ namespace FireflyIII\Notifications\Admin;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification; use Illuminate\Notifications\Notification;
/**
* Class UserRegistration
*/
class UserRegistration extends Notification class UserRegistration extends Notification
{ {
use Queueable; use Queueable;
@ -46,18 +50,18 @@ class UserRegistration extends Notification
/** /**
* Get the notification's delivery channels. * Get the notification's delivery channels.
* *
* @param mixed $notifiable * @param mixed $notifiable
* @return array * @return array
*/ */
public function via($notifiable) public function via($notifiable)
{ {
return ['mail']; return ['mail', 'slack'];
} }
/** /**
* Get the mail representation of the notification. * Get the mail representation of the notification.
* *
* @param mixed $notifiable * @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage * @return \Illuminate\Notifications\Messages\MailMessage
*/ */
public function toMail($notifiable) public function toMail($notifiable)
@ -67,10 +71,21 @@ class UserRegistration extends Notification
->subject((string) trans('email.registered_subject_admin')); ->subject((string) trans('email.registered_subject_admin'));
} }
/**
* Get the Slack representation of the notification.
*
* @param mixed $notifiable
* @return SlackMessage
*/
public function toSlack($notifiable)
{
return (new SlackMessage)->content((string) trans('email.admin_new_user_registered', ['email' => $this->user->email, 'id' => $this->user->id]));
}
/** /**
* Get the array representation of the notification. * Get the array representation of the notification.
* *
* @param mixed $notifiable * @param mixed $notifiable
* @return array * @return array
*/ */
public function toArray($notifiable) public function toArray($notifiable)

View File

@ -22,9 +22,9 @@ declare(strict_types=1);
namespace FireflyIII\Notifications\Admin; namespace FireflyIII\Notifications\Admin;
use FireflyIII\Models\Bill;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification; use Illuminate\Notifications\Notification;
/** /**
@ -35,7 +35,7 @@ class VersionCheckResult extends Notification
{ {
use Queueable; use Queueable;
private string $message; private string $message;
/** /**
* Create a new notification instance. * Create a new notification instance.
@ -55,7 +55,7 @@ class VersionCheckResult extends Notification
*/ */
public function via($notifiable) public function via($notifiable)
{ {
return ['mail']; return ['mail', 'slack'];
} }
/** /**
@ -69,7 +69,21 @@ class VersionCheckResult extends Notification
return (new MailMessage) return (new MailMessage)
->markdown('emails.new-version', ['message' => $this->message]) ->markdown('emails.new-version', ['message' => $this->message])
->subject((string)trans('email.new_version_email_subject')); ->subject((string) trans('email.new_version_email_subject'));
}
/**
* Get the Slack representation of the notification.
*
* @param mixed $notifiable
* @return SlackMessage
*/
public function toSlack($notifiable)
{
return (new SlackMessage)->content($this->message)
->attachment(function ($attachment) {
$attachment->title('Firefly III @ GitHub', 'https://github.com/firefly-iii/firefly-iii/releases');
});
} }
/** /**

View File

@ -25,6 +25,7 @@ namespace FireflyIII\Notifications\User;
use FireflyIII\Models\Bill; use FireflyIII\Models\Bill;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification; use Illuminate\Notifications\Notification;
/** /**
@ -58,7 +59,7 @@ class BillReminder extends Notification
*/ */
public function via($notifiable) public function via($notifiable)
{ {
return ['mail']; return ['mail', 'slack'];
} }
/** /**
@ -79,6 +80,28 @@ class BillReminder extends Notification
->subject($subject); ->subject($subject);
} }
/**
* Get the Slack representation of the notification.
*
* @param mixed $notifiable
* @return SlackMessage
*/
public function toSlack($notifiable)
{
$message = (string) trans(sprintf('email.bill_warning_subject_%s', $this->field), ['diff' => $this->diff, 'name' => $this->bill->name]);
if (0 === $this->diff) {
$message = (string) trans(sprintf('email.bill_warning_subject_now_%s', $this->field), ['diff' => $this->diff, 'name' => $this->bill->name]);
}
$bill = $this->bill;
$url = route('bills.show', [$bill->id]);
return (new SlackMessage)
->warning()
->attachment(function ($attachment) use ($bill, $url) {
$attachment->title((string) trans('firefly.visit_bill', ['name' => $bill->name]), $url);
})
->content($message);
}
/** /**
* Get the array representation of the notification. * Get the array representation of the notification.
* *

View File

@ -48,6 +48,9 @@ use FireflyIII\Models\TransactionGroup;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\UserGroup; use FireflyIII\Models\UserGroup;
use FireflyIII\Models\Webhook; use FireflyIII\Models\Webhook;
use FireflyIII\Notifications\Admin\TestNotification;
use FireflyIII\Notifications\Admin\UserRegistration;
use FireflyIII\Notifications\Admin\VersionCheckResult;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\BelongsToMany;
@ -578,4 +581,25 @@ class User extends Authenticatable
}; };
} }
/**
* Route notifications for the Slack channel.
*
* @param Notification $notification
* @return string
*/
public function routeNotificationForSlack(Notification $notification): string
{
// this check does not validate if the user is owner, Should be done by notification itself.
if ($notification instanceof TestNotification) {
return app('fireflyconfig')->get('slack_webhook_url', '')->data;
}
if ($notification instanceof UserRegistration) {
return app('fireflyconfig')->get('slack_webhook_url', '')->data;
}
if ($notification instanceof VersionCheckResult) {
return app('fireflyconfig')->get('slack_webhook_url', '')->data;
}
return app('preferences')->getForUser($this, 'slack_webhook_url', '')->data;
}
} }

View File

@ -1051,6 +1051,10 @@ return [
'pref_notification_user_login' => 'Alert when you login from a new location', 'pref_notification_user_login' => 'Alert when you login from a new location',
'pref_notifications' => 'Notifications', 'pref_notifications' => 'Notifications',
'pref_notifications_help' => 'Indicate if these are notifications you would like to get. Some notifications may contain sensitive financial information.', 'pref_notifications_help' => 'Indicate if these are notifications you would like to get. Some notifications may contain sensitive financial information.',
'slack_webhook_url' => 'Slack Webhook URL',
'slack_webhook_url_help' => 'If you want Firefly III to notify you using Slack, enter the webhook URL here. Otherwise leave the field blank. If you are an admin, you need to set this URL in the administration as well.',
'slack_url_label' => 'Slack "incoming webhook" URL',
// profile: // profile:
'delete_stuff_header' => 'Delete data', 'delete_stuff_header' => 'Delete data',
'permanent_delete_stuff' => 'Be careful with these buttons. Deleting stuff is permanent.', 'permanent_delete_stuff' => 'Be careful with these buttons. Deleting stuff is permanent.',
@ -1349,6 +1353,7 @@ return [
// bills: // bills:
'not_expected_period' => 'Not expected this period', 'not_expected_period' => 'Not expected this period',
'not_or_not_yet' => 'Not (yet)', 'not_or_not_yet' => 'Not (yet)',
'visit_bill' => 'Visit bill ":name" at Firefly III',
'match_between_amounts' => 'Bill matches transactions between :low and :high.', 'match_between_amounts' => 'Bill matches transactions between :low and :high.',
'running_again_loss' => 'Previously linked transactions to this bill may lose their connection, if they (no longer) match the rule(s).', 'running_again_loss' => 'Previously linked transactions to this bill may lose their connection, if they (no longer) match the rule(s).',
'bill_related_rules' => 'Rules related to this bill', 'bill_related_rules' => 'Rules related to this bill',
@ -1978,7 +1983,7 @@ return [
'delete_user' => 'Delete user :email', 'delete_user' => 'Delete user :email',
'user_deleted' => 'The user has been deleted', 'user_deleted' => 'The user has been deleted',
'send_test_email' => 'Send test email message', 'send_test_email' => 'Send test email message',
'send_test_email_text' => 'To see if your installation is capable of sending email, please press this button. You will not see an error here (if any), <strong>the log files will reflect any errors</strong>. You can press this button as many times as you like. There is no spam control. The message will be sent to <code>:email</code> and should arrive shortly.', 'send_test_email_text' => 'To see if your installation is capable of sending email or posting Slack messages, please press this button. You will not see an error here (if any), <strong>the log files will reflect any errors</strong>. You can press this button as many times as you like. There is no spam control. The message will be sent to <code>:email</code> and should arrive shortly.',
'send_message' => 'Send message', 'send_message' => 'Send message',
'send_test_triggered' => 'Test was triggered. Check your inbox and the log files.', 'send_test_triggered' => 'Test was triggered. Check your inbox and the log files.',
'give_admin_careful' => 'Users who are given admin rights can take away yours. Be careful.', 'give_admin_careful' => 'Users who are given admin rights can take away yours. Be careful.',
@ -1986,9 +1991,10 @@ return [
'admin_maintanance_expl' => 'Some nifty buttons for Firefly III maintenance', 'admin_maintanance_expl' => 'Some nifty buttons for Firefly III maintenance',
'admin_maintenance_clear_cache' => 'Clear cache', 'admin_maintenance_clear_cache' => 'Clear cache',
'admin_notifications' => 'Admin notifications', 'admin_notifications' => 'Admin notifications',
'admin_notifications_expl' => 'The following notifications can be enabled or disabled by the administrator.', 'admin_notifications_expl' => 'The following notifications can be enabled or disabled by the administrator. If you want to get these messages over Slack as well, set the "incoming webhook" URL.',
'admin_notification_check_user_new_reg' => 'User gets post-registration welcome message', 'admin_notification_check_user_new_reg' => 'User gets post-registration welcome message',
'admin_notification_check_admin_new_reg' => 'Administrator(s) get new user registration notification', 'admin_notification_check_admin_new_reg' => 'Administrator(s) get new user registration notification',
'admin_notification_check_new_version' => 'A new version is available',
'save_notification_settings' => 'Save settings', 'save_notification_settings' => 'Save settings',
'notification_settings_saved' => 'The notification settings have been saved', 'notification_settings_saved' => 'The notification settings have been saved',

View File

@ -47,6 +47,7 @@
</label> </label>
</div> </div>
{% endfor %} {% endfor %}
{{ ExpandedForm.text('slackUrl', slackUrl, {'label' : 'slack_url_label'|_}) }}
</div> </div>
<div class="box-footer"> <div class="box-footer">
<button type="submit" class="btn btn-success"> <button type="submit" class="btn btn-success">

View File

@ -260,6 +260,13 @@
{% endfor %} {% endfor %}
</div> </div>
</div> </div>
<div class="col-lg-6 col-md-6 col-sm-12 col-xs-12">
<div class="preferences-box">
<h3>{{ 'slack_webhook_url'|_ }}</h3>
<p class="text-info">{{ 'slack_webhook_url_help'|_ }}</p>
{{ ExpandedForm.text('slackUrl',slackUrl,{'label' : 'slack_url_label'|_}) }}
</div>
</div>
</div> </div>
</div> </div>
</div> </div>