From 99e5f82e1796ea063197118af2e7c67e4b21c169 Mon Sep 17 00:00:00 2001 From: Iwan Gurjanow Date: Wed, 8 Mar 2023 11:58:58 +0100 Subject: [PATCH] Initial plugin commit v1.0.0 --- .DS_Store | Bin 0 -> 8196 bytes .gitignore | 1 + .idea/.gitignore | 8 ++ .idea/debug-log-monitoring.iml | 8 ++ .idea/modules.xml | 8 ++ .idea/php.xml | 22 ++++ .idea/vcs.xml | 6 + deactivation-hook.php | 9 ++ debug-log-monitoring.config.example.php | 65 +++++++++++ debug-log-monitoring.php | 137 +++++++++++++++++++++++ readme.md | 139 ++++++++++++++++++++++++ readme.txt | 139 ++++++++++++++++++++++++ utils/.DS_Store | Bin 0 -> 6148 bytes utils/cron-schedules.php | 17 +++ utils/log.php | 6 + utils/message-builder.php | 71 ++++++++++++ zip/.DS_Store | Bin 0 -> 6148 bytes zip/debug-log-monitoring-v-1.0.0.zip | Bin 0 -> 24889 bytes 18 files changed, 636 insertions(+) create mode 100644 .DS_Store create mode 100644 .gitignore create mode 100644 .idea/.gitignore create mode 100644 .idea/debug-log-monitoring.iml create mode 100644 .idea/modules.xml create mode 100644 .idea/php.xml create mode 100644 .idea/vcs.xml create mode 100644 deactivation-hook.php create mode 100644 debug-log-monitoring.config.example.php create mode 100644 debug-log-monitoring.php create mode 100644 readme.md create mode 100644 readme.txt create mode 100644 utils/.DS_Store create mode 100644 utils/cron-schedules.php create mode 100644 utils/log.php create mode 100644 utils/message-builder.php create mode 100644 zip/.DS_Store create mode 100644 zip/debug-log-monitoring-v-1.0.0.zip diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..af1a5f8d4e03e428c1489daad23570bb02958f51 GIT binary patch literal 8196 zcmeHML2DC16n@jDc4H87un=zx9=z1rR1u1om?mIRR9w-6O3h~1ChhLdl583=6mk^( z4gLi0{s6&$ARfH=L-czy)3i1ziA6+lrp$b^Gw++(_st{2+Y*te_JT4|iHICj#$o}@ zgu?S&$I6@;Spo|1i85-_9yJt>ncD%YfK|XMU=^?mSOxw91#o9`aZb4R^{j2J0#R8*{~vj<_Q!lW2N(=qR|Ij~}I zs8Q2NXgUeAvoINoP`iWYDsvJQjkdK4SOvxv;M{$Q0t%6}H-F0CB_(v3S|steODmM3 zpKeC7=||EBtN8s`!Pq6Wl21L_1~&qeF$O01GLQi~zyzZ!*o{YD;F+I7dWkoPu}g&h zY`c|yVYgY{ev$6bCT&oi?vh9KDZXBSRR~W*_~g<(v;q9lPqFfrM}6AC*e-kzDG9pG z$d@wWV>ksp+Ae!d8K+D)_&Irj^pxm}hwi%0U?* zC^#Tu3Z@*$*<*|hA(c(xEZ_k7qg6!R*)0!_k*}h{;R1+)+lc8D%Y{cx=IIrNQor9# zjqV@(G-`LV)z!~pZa%kgX3<%63eGdHCHr31$a>vsqw|EH9wJ!$gMPz*)DHVw#q%37 z$r|k>>?lGz2x0Q{aXSfQzbbo(GR5`ugp+sjTgB2~P+nhKcG2uFyMy($)n)hE)$9BF zd1vY3l^YMXcB43vZ*)`%c8tq84Wsrgdd8ax4o(P9V|+B^zHNMZXF_f?dl*LJvlBwY z@b!0652R AL;wH) literal 0 HcmV?d00001 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d84158a --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +debug-log-monitoring.config.php \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/debug-log-monitoring.iml b/.idea/debug-log-monitoring.iml new file mode 100644 index 0000000..c956989 --- /dev/null +++ b/.idea/debug-log-monitoring.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..9bfa21b --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/php.xml b/.idea/php.xml new file mode 100644 index 0000000..133d465 --- /dev/null +++ b/.idea/php.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/deactivation-hook.php b/deactivation-hook.php new file mode 100644 index 0000000..f631202 --- /dev/null +++ b/deactivation-hook.php @@ -0,0 +1,9 @@ + "My awesome app", + "interval" => "24_hours", + "maxStoreAgeInDays" => 28, + "logsFolder" => "debug-logs", + "clearLog" => true, + "configVersion" => "1.0.0", + "notifications" => [ + "enabled" => false, + "logFilters" => [ + [ + "filterKey" => "PHP Fatal error:", + "notificationString" => "%d PHP Fatal errors", + "emojis" => [ + [ + "threshold" => 10, + "slack" => ":surprised-pikachu:", + "email" => "😤" + ], + [ + "threshold" => 0, + "slack" => ":white_check_mark:", + "email" => "😎" + ] + ] + ], + [ + "filterKey" => "You have an error in your SQL syntax;", + "notificationString" => "%d SQL syntax errors", + "emojis" => [ + [ + "threshold" => 0, + "slack" => ":white_check_mark:", + "email" => "😎" + ], + [ + "threshold" => 10, + "slack" => ":pepesad:", + "email" => "😤" + ] + ] + ] + ], + "channels" => [ + "slack" => [ + "enabled" => false, + "webhookUrl" => "" + ], + "email" => [ + "enabled" => false, + "recipients" => [ + "your@mail.com", + ], + "sender" => "Awesome App ", + "subject" => "My awesome App debug log report" + ] + ] + ] + ]; +} + diff --git a/debug-log-monitoring.php b/debug-log-monitoring.php new file mode 100644 index 0000000..6f5ca12 --- /dev/null +++ b/debug-log-monitoring.php @@ -0,0 +1,137 @@ += $maxAge) { + unlink($file); + } + } + } + + // Send slack message with summary + if ($config['notifications']['enabled']) { + dlm_doNotify($logFileNew); + } + else { + dlm_log('Skipping notification because notifications are disabled in config.'); + } + + dlm_log('End debug log procedure'); +} + +function dlm_doNotify(string $logFilePath): void { + $config = dlm_getConfig(); + $channels = $config['notifications']['channels']; + $slackEnabled = $channels['slack']['enabled']; + $emailEnabled = $channels['email']['enabled']; + $isAnyChannelEnabled = $slackEnabled || $emailEnabled; + if (!$isAnyChannelEnabled) { + dlm_log('Skipping notification because no notification channel is enabled in config.'); + return; + } + + if ($slackEnabled) { + dlm_doNotifySlack($logFilePath); + } + + if ($emailEnabled) { + dlm_doNotifyEmail($logFilePath); + } + + dlm_log('Notification sent.'); +} + +function dlm_doNotifySlack(string $logFilePath): void { + dlm_log('Sending slack notification...'); + $config = dlm_getConfig(); + $slackChannel = $config['notifications']['channels']['slack']; + $message = dlm_buildMessage($logFilePath, 'slack'); + $data = array( + 'text' => $message, + ); + $data = json_encode($data); + // json encode escapes automatically slashes, so we need to unescape them + $data = str_replace('\\\n', '\n', $data); + $curl = curl_init($slackChannel['webhookUrl']); + curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "POST"); + curl_setopt($curl, CURLOPT_POSTFIELDS, $data); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + curl_setopt($curl, CURLOPT_HTTPHEADER, array( + 'Content-Type: application/json', + 'Content-Length: ' . strlen($data) + )); + $result = curl_exec($curl); + curl_close($curl); + dlm_log('End Slack Notification with log summary'); +} + +function dlm_doNotifyEmail(string $logFilePath): void { + dlm_log('Sending email notification...'); + $config = dlm_getConfig(); + $emailChannel = $config['notifications']['channels']['email']; + $message = dlm_buildMessage($logFilePath, 'email'); + $sender = $emailChannel['sender']; + $headers = ["FROM: $sender", 'Content-Type: text/html; charset=UTF-8']; + $result = wp_mail($emailChannel['recipients'], $emailChannel['subject'], $message, $headers); + if (!$result) { + dlm_log('Error sending email notification.', E_USER_WARNING); + } + else { + dlm_log('End Email Notification with log summary'); + } +} + +if ( ! wp_next_scheduled( 'dlm_cron_hook_process_log' ) ) { + $config = dlm_getConfig(); + wp_schedule_event( time(), $config["interval"], 'dlm_cron_hook_process_log' ); +} + +register_deactivation_hook( __FILE__, 'dlm_deactivate' ); diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..0841369 --- /dev/null +++ b/readme.md @@ -0,0 +1,139 @@ +# Debug Log Monitoring (DLM) Plugin +__Disclaimer:__ You will need FTP access to your server to correctly use this plugin. + +## Description +This plugin is for developers who want to monitor their debug.log file. Its purpose is to create a scheduled task +that splits the main `debug.log` file into interval based (e.g. daily, weekly) files. +Also it provides a simple way to send a customized log summary notification via email or Slack. + +## Installation + +### Step 1: Install the plugin via WordPress Plugin Management +__Do not activate it directly!! Follow Step 2 and Step 3 first.__ + +### Step 2: Activate WordPress debug logging +In order to use this plugin you need to activate WordPress debug logging. Add the following to your `wp-config.php` file: + + define( 'WP_DEBUG', true ); + define( 'WP_DEBUG_LOG', true ); + define( 'WP_DEBUG_DISPLAY', false ); + @ini_set( 'display_errors', 0 ); + +### Step 3: Configure the plugin +It is important to customize the configuration before activating the plugin. + +Navigate to `wp-content/plugins/debug-log-monitoring` directory using a FTP Client, duplicate the ``debug-log-monitoring.config.example.php`` file +and rename it to ``debug-log-monitoring.config.php``. Adjust the configuration settings to your needs. + +For a detailed explanation of all options, see section **Config** below. + +### Step 4: Activate the plugin +Activate the plugin via the WordPress Plugin Management. + +*Note*: The scheduler usually runs the first time directly after activation. And the next time after the configured interval. +If you want to change the config and re-run the scheduler, please follow the steps below. +- Deactivate the plugin (this will reset and remove the scheduler) +- Adjust the config +- Activate the plugin + +## Config + +Following settings can be configured: +### Base Settings +#### appName - string +The name of your application or website. It is used to build the reporting summary message body. + +#### interval - string +Sets the time interval between two runs of the debug log monitoring. Valid values: +- "12_hours" +- "24_hours" +- "2_days" +- "1_week" + +#### maxStoreAgeInDays - int (default 28) +How many days the logs should be saved on disc before automatically removed. + +#### logsFolder - string (default "debug-logs") +The folder, in which the created logs will be stored - relative to `wp-content/`. + +#### clearLog - boolean (default true) +Truncate (clear) the main ``debug.log`` file after the interval log file has been created with the debug logs content. + +#### configVersion - string +Helps the plugin to validate, if the configuration needs to be updated, in case the plugin updates. Please check provided changelog in this case. + +### Notifications Settings +#### notifications.enabled - boolean +Enable sending a reporting summary via different channels. Before enabling, make sure at least one channel is properly configured. + +#### notifications.logFilters - LogFilter[] +An array of LogFilter sub arrays (associative) that define for which string occurences the report should be scanned. This is the base for +creating the report. See example below. + +#### notifications.logFilters[0].filterKey - string +The string sequence that should be counted within the log. A value of e.g. "PHP Fatal error:" will search the log for all occurences of php fatal errors. + +#### notifications.logFilters[0].notificationString - string +The string that should be used in the notification message. E.g. "%d PHP Fatal Errors". The %d will be replaced with the actual count of the filterKey. + +#### notifications.logFilters[0].emojis - Emoji[] +An array of Emoji sub arrays (associative) that define which emojis should be used in the notification message. See example below. + +#### notifications.logFilters[0].emojis[0].threshold - number +The threshold that defines, if the emoji should be used. E.g. if the threshold is 10 and the filterKey was found 5 times, the emoji will not be used. + +#### notifications.logFilters[0].emojis[0].slack - string +The emoji string that should be appended in the slack notification message. E.g. ":fire:". + +#### notifications.logFilters[0].emojis[0].email - string +The emoji string that should be appended in the email notification message. E.g. "🔥". + +### Notification Channel Settings +#### notifications.channels.slack.enabled - boolean +Enable sending a reporting summary via slack. + +#### notifications.channels.slack.webhookUrl - string +The slack webhook url to send the notification to. + +#### notifications.channels.email.enabled - boolean +Enable sending a reporting summary via email. + +#### notifications.channels.email.recipients - string[] +An array of email addresses that should receive the notification. + +#### notifications.channels.email.sender - string +The email address that should be used as sender. Please note: If you are using an SMPT Plugin like WP Mail SMTP, the sender address will be overwritten by the plugin. + +#### notifications.channels.email.subject - string +The subject of the email notification. + +## FAQs + +### How can I see the created log files? +The created log files are stored in the folder defined in the config option `logsFolder`. The default is `wp-content/debug-logs`. +Connect to your server via FTP and navigate to the folder. You should see the created log files. + +### What is the slack webhook url? And how can I get one? +The slack webhook url is a unique url that is used to send a message to a specific slack channel. +Please follow the instructions on the slack website to create a webhook url: https://api.slack.com/messaging/webhooks + +### I am not receiving any emails locally. What can I do? +If you are using a local development environment, you might not receive any emails. This is because the plugin uses the WordPress function `wp_mail()`. +We recommend to use a plugin like WP Mail SMTP to send emails locally. This way emails are also less likely to be marked as spam. + +### Is it possible to start the scheduler at a specific time? +The schedular usually runs the first time directly after activation. Currently, there is no way to define a start time. +As a workaround, you can deactivate the plugin, and activate it again at the specific time. + +### Is there a UI to configure the plugin? +Currently, there is no UI to configure the plugin. All configuration is done via the config file. + +### How can I add messages to the log from within my plugin or theme? +You can easily add messages with the PHP build-in function + +``trigger_error(string $message, int $error_level = E_USER_NOTICE): bool`` + +See here: https://www.php.net/manual/en/function.trigger-error.php + +### Question not listed here? +Please open an issue on GitHub: \ No newline at end of file diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..0841369 --- /dev/null +++ b/readme.txt @@ -0,0 +1,139 @@ +# Debug Log Monitoring (DLM) Plugin +__Disclaimer:__ You will need FTP access to your server to correctly use this plugin. + +## Description +This plugin is for developers who want to monitor their debug.log file. Its purpose is to create a scheduled task +that splits the main `debug.log` file into interval based (e.g. daily, weekly) files. +Also it provides a simple way to send a customized log summary notification via email or Slack. + +## Installation + +### Step 1: Install the plugin via WordPress Plugin Management +__Do not activate it directly!! Follow Step 2 and Step 3 first.__ + +### Step 2: Activate WordPress debug logging +In order to use this plugin you need to activate WordPress debug logging. Add the following to your `wp-config.php` file: + + define( 'WP_DEBUG', true ); + define( 'WP_DEBUG_LOG', true ); + define( 'WP_DEBUG_DISPLAY', false ); + @ini_set( 'display_errors', 0 ); + +### Step 3: Configure the plugin +It is important to customize the configuration before activating the plugin. + +Navigate to `wp-content/plugins/debug-log-monitoring` directory using a FTP Client, duplicate the ``debug-log-monitoring.config.example.php`` file +and rename it to ``debug-log-monitoring.config.php``. Adjust the configuration settings to your needs. + +For a detailed explanation of all options, see section **Config** below. + +### Step 4: Activate the plugin +Activate the plugin via the WordPress Plugin Management. + +*Note*: The scheduler usually runs the first time directly after activation. And the next time after the configured interval. +If you want to change the config and re-run the scheduler, please follow the steps below. +- Deactivate the plugin (this will reset and remove the scheduler) +- Adjust the config +- Activate the plugin + +## Config + +Following settings can be configured: +### Base Settings +#### appName - string +The name of your application or website. It is used to build the reporting summary message body. + +#### interval - string +Sets the time interval between two runs of the debug log monitoring. Valid values: +- "12_hours" +- "24_hours" +- "2_days" +- "1_week" + +#### maxStoreAgeInDays - int (default 28) +How many days the logs should be saved on disc before automatically removed. + +#### logsFolder - string (default "debug-logs") +The folder, in which the created logs will be stored - relative to `wp-content/`. + +#### clearLog - boolean (default true) +Truncate (clear) the main ``debug.log`` file after the interval log file has been created with the debug logs content. + +#### configVersion - string +Helps the plugin to validate, if the configuration needs to be updated, in case the plugin updates. Please check provided changelog in this case. + +### Notifications Settings +#### notifications.enabled - boolean +Enable sending a reporting summary via different channels. Before enabling, make sure at least one channel is properly configured. + +#### notifications.logFilters - LogFilter[] +An array of LogFilter sub arrays (associative) that define for which string occurences the report should be scanned. This is the base for +creating the report. See example below. + +#### notifications.logFilters[0].filterKey - string +The string sequence that should be counted within the log. A value of e.g. "PHP Fatal error:" will search the log for all occurences of php fatal errors. + +#### notifications.logFilters[0].notificationString - string +The string that should be used in the notification message. E.g. "%d PHP Fatal Errors". The %d will be replaced with the actual count of the filterKey. + +#### notifications.logFilters[0].emojis - Emoji[] +An array of Emoji sub arrays (associative) that define which emojis should be used in the notification message. See example below. + +#### notifications.logFilters[0].emojis[0].threshold - number +The threshold that defines, if the emoji should be used. E.g. if the threshold is 10 and the filterKey was found 5 times, the emoji will not be used. + +#### notifications.logFilters[0].emojis[0].slack - string +The emoji string that should be appended in the slack notification message. E.g. ":fire:". + +#### notifications.logFilters[0].emojis[0].email - string +The emoji string that should be appended in the email notification message. E.g. "🔥". + +### Notification Channel Settings +#### notifications.channels.slack.enabled - boolean +Enable sending a reporting summary via slack. + +#### notifications.channels.slack.webhookUrl - string +The slack webhook url to send the notification to. + +#### notifications.channels.email.enabled - boolean +Enable sending a reporting summary via email. + +#### notifications.channels.email.recipients - string[] +An array of email addresses that should receive the notification. + +#### notifications.channels.email.sender - string +The email address that should be used as sender. Please note: If you are using an SMPT Plugin like WP Mail SMTP, the sender address will be overwritten by the plugin. + +#### notifications.channels.email.subject - string +The subject of the email notification. + +## FAQs + +### How can I see the created log files? +The created log files are stored in the folder defined in the config option `logsFolder`. The default is `wp-content/debug-logs`. +Connect to your server via FTP and navigate to the folder. You should see the created log files. + +### What is the slack webhook url? And how can I get one? +The slack webhook url is a unique url that is used to send a message to a specific slack channel. +Please follow the instructions on the slack website to create a webhook url: https://api.slack.com/messaging/webhooks + +### I am not receiving any emails locally. What can I do? +If you are using a local development environment, you might not receive any emails. This is because the plugin uses the WordPress function `wp_mail()`. +We recommend to use a plugin like WP Mail SMTP to send emails locally. This way emails are also less likely to be marked as spam. + +### Is it possible to start the scheduler at a specific time? +The schedular usually runs the first time directly after activation. Currently, there is no way to define a start time. +As a workaround, you can deactivate the plugin, and activate it again at the specific time. + +### Is there a UI to configure the plugin? +Currently, there is no UI to configure the plugin. All configuration is done via the config file. + +### How can I add messages to the log from within my plugin or theme? +You can easily add messages with the PHP build-in function + +``trigger_error(string $message, int $error_level = E_USER_NOTICE): bool`` + +See here: https://www.php.net/manual/en/function.trigger-error.php + +### Question not listed here? +Please open an issue on GitHub: \ No newline at end of file diff --git a/utils/.DS_Store b/utils/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..7364332dae31d73942be644ddcdf7077d1933685 GIT binary patch literal 6148 zcmeHK!A`?441I=4OyW|(g=2mIB>te9wgYE=0J=_q)X`~G@BQ=f92>--hn-M`Y$`){n#2HP(bxAt0btIiD5{Kznn#Du-1(d+ImazJtnrEs>XAfKH0j!}xaK`tyyN@& zdopF literal 0 HcmV?d00001 diff --git a/utils/cron-schedules.php b/utils/cron-schedules.php new file mode 100644 index 0000000..e7cd583 --- /dev/null +++ b/utils/cron-schedules.php @@ -0,0 +1,17 @@ + 60 * 60 * 12, + 'display' => esc_html__( 'Every twelve hours' ), ); + $schedules['24_hours'] = array( + 'interval' => 60 * 60 * 24, + 'display' => esc_html__( 'Every twenty four hours' ), ); + $schedules['2_days'] = array( + 'interval' => 60 * 60 * 24 * 2, + 'display' => esc_html__( 'Every two days' ), ); + $schedules['1_week'] = array( + 'interval' => 60 * 60 * 24 * 7, + 'display' => esc_html__( 'Every seven days' ), ); + return $schedules; +} \ No newline at end of file diff --git a/utils/log.php b/utils/log.php new file mode 100644 index 0000000..e905c7f --- /dev/null +++ b/utils/log.php @@ -0,0 +1,6 @@ + [ + "slack" => "*%s*", + "email" => "%s", + ], + ]; + $appName = $config['appName']; + $intervalDisplay = str_replace('_', ' ', $config['interval']); + $appNameStrong = sprintf($channelMarkup['strong'][$channel], $appName); + $numOfLinesStrong = sprintf($channelMarkup['strong'][$channel], $linesCount); + + return sprintf("$appNameStrong: the debug log for the last %s has $numOfLinesStrong lines:", $intervalDisplay); +} + +function dlm_getMessageBody(string $logFilePath, string $channel): string { + $config = dlm_getConfig(); + $logFilters = $config['notifications']['logFilters']; + $logFileContent = file_get_contents($logFilePath); + $message = ''; + $channelMarkup = [ + "bulletPoint" => [ + "slack" => "\n- %s", + "email" => "
  • %s
  • ", + ], + ]; + + foreach ($logFilters as $logFilter) { + $filterKey = $logFilter['filterKey']; + $logFilterCount = substr_count($logFileContent, $filterKey); + $notificationString = sprintf($logFilter['notificationString'], $logFilterCount); + $emojis = $logFilter['emojis']; + + if (count($emojis) > 0) { + // sort $emojis array by attribute threshold + usort($emojis, function($a, $b) { + return $a['threshold'] <=> $b['threshold']; + }); + + // filter emojis where logFilterCount is greater or equal to threshold + $emojis = array_filter($emojis, function($emoji) use ($logFilterCount) { + return $logFilterCount >= $emoji['threshold']; + }); + + // check if emojis contains any elements + + $emojisCount = count($emojis); + if ($emojisCount > 0) { + $emoji = $emojis[$emojisCount - 1]; + $notificationString .= " " . $emoji[$channel]; + } + } + + $message .= sprintf($channelMarkup['bulletPoint'][$channel], $notificationString); + } + + if ($channel === 'email') { + $message = "
      $message
    "; + } + return $message; +} \ No newline at end of file diff --git a/zip/.DS_Store b/zip/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..d6022778030e5df8b3268254956ebe950c8086f8 GIT binary patch literal 6148 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 a-(Z^%bqaj32LHE$f5}mjkYHfIr~d$qYeHxM literal 0 HcmV?d00001