├── .gitignore
├── README.md
├── acp
├── discord_notifications_info.php
└── discord_notifications_module.php
├── adm
└── style
│ └── acp_discord_notifications.html
├── build.xml
├── composer.json
├── config
├── parameters.yml
└── services.yml
├── event
└── notification_event_listener.php
├── language
├── en
│ ├── acp_discord_notifications.php
│ ├── discord_notification_messages.php
│ └── info_acp_discord_notifications.php
└── fr
│ ├── acp_discord_notifications.php
│ ├── discord_notification_messages.php
│ └── info_acp_discord_notifications.php
├── license.txt
├── migrations
└── extension_installation.php
├── notification_service.php
├── phpunit.xml.dist
├── tests
├── dbal
│ ├── fixtures
│ │ └── config.xml
│ └── simple_test.php
└── functional
│ └── demo_test.php
└── travis
└── prepare-phpbb.sh
/.gitignore:
--------------------------------------------------------------------------------
1 | build/
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # phpbb-discord-notifications
2 |
3 | A phpBB extension that publishes notification messages to a Discord channel when certain events occur on a phpBB board. The intent of this extension is meant to announce content changes on a forum to a community residing on a Discord server. It is not intended as a compliment to the announcements found within the phpBB admin or moderator control panels. See the [wiki](https://github.com/rootslinux/phpbb-discord-notifications/wiki) for additional information.
4 |
5 | ## Installation
6 |
7 | Copy the extension to phpBB/ext/roots/discordnotifications
8 |
9 | Go to "ACP" > "Customise" > "Extensions" and enable the "Discord Notifications" extension.
10 |
11 | ## Tests and Continuous Integration
12 |
13 | We use Travis-CI as a continuous integration server and phpunit for our unit testing. See more information on the [phpBB Developer Docs](https://area51.phpbb.com/docs/dev/31x/testing/index.html).
14 | To run the tests locally, you need to install phpBB from its Git repository. Afterwards run the following command from the phpBB Git repository's root:
15 |
16 | Windows:
17 |
18 | phpBB\vendor\bin\phpunit.bat -c phpBB\ext\roots\discordnotifications\phpunit.xml.dist
19 |
20 | Other Systems:
21 |
22 | phpBB/vendor/bin/phpunit -c phpBB/ext/roots/discordnotifications/phpunit.xml.dist
23 |
24 | ## License
25 |
26 | [GPLv2](license.txt)
27 |
--------------------------------------------------------------------------------
/acp/discord_notifications_info.php:
--------------------------------------------------------------------------------
1 | '\roots\discordnotifications\acp\discord_notifications_module',
20 | 'title' => 'ACP_DISCORD_NOTIFICATIONS',
21 | 'modes' => array(
22 | 'settings' => array(
23 | 'title' => 'ACP_DISCORD_NOTIFICATIONS_TITLE',
24 | 'auth' => 'ext_roots/discordnotifications && acl_a_board',
25 | 'cat' => array('ACP_DISCORD_NOTIFICATIONS')
26 | ),
27 | ),
28 | );
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/acp/discord_notifications_module.php:
--------------------------------------------------------------------------------
1 | cache = $phpbb_container->get('cache.driver');
71 | $this->config = $phpbb_container->get('config');
72 | $this->db = $phpbb_container->get('dbal.conn');
73 | $this->language = $phpbb_container->get('language');
74 | $this->log = $phpbb_container->get('log');
75 | $this->request = $phpbb_container->get('request');
76 | $this->template = $phpbb_container->get('template');
77 | $this->user = $phpbb_container->get('user');
78 | // Used for sending test messages to Discord
79 | $this->notification_service = $phpbb_container->get('roots.discordnotifications.notification_service');
80 |
81 | $this->user->add_lang_ext('roots/discordnotifications', 'acp_discord_notifications');
82 | $this->tpl_name = 'acp_discord_notifications';
83 | $this->page_title = $this->user->lang('ACP_DISCORD_NOTIFICATIONS_TITLE');
84 |
85 | add_form_key(self::PAGE_FORM_NAME);
86 |
87 | // Process submit actions
88 | if ($this->request->is_set_post('action_send_test_message'))
89 | {
90 | $this->process_send_test_message();
91 | }
92 | elseif ($this->request->is_set_post('submit'))
93 | {
94 | $this->process_form_submit();
95 | }
96 |
97 | // Generate the dynamic HTML content for enabling/disabling forum notifications
98 | $this->generate_forum_section();
99 |
100 | // Assign template values so that the page reflects the state of the extension settings
101 | $this->template->assign_vars(array(
102 | 'DN_MASTER_ENABLE' => $this->config['discord_notifications_enabled'],
103 | 'DN_WEBHOOK_URL' => $this->config['discord_notifications_webhook_url'],
104 | 'DN_POST_PREVIEW_LENGTH' => $this->config['discord_notifications_post_preview_length'],
105 | 'DN_TEST_MESSAGE_TEXT' => $this->user->lang('DN_TEST_MESSAGE_TEXT'),
106 |
107 | 'DN_POST_CREATE' => $this->config['discord_notification_type_post_create'],
108 | 'DN_POST_UPDATE' => $this->config['discord_notification_type_post_update'],
109 | 'DN_POST_DELETE' => $this->config['discord_notification_type_post_delete'],
110 | 'DN_POST_LOCK' => $this->config['discord_notification_type_post_lock'],
111 | 'DN_POST_UNLOCK' => $this->config['discord_notification_type_post_unlock'],
112 |
113 | 'DN_TOPIC_CREATE' => $this->config['discord_notification_type_topic_create'],
114 | 'DN_TOPIC_UPDATE' => $this->config['discord_notification_type_topic_update'],
115 | 'DN_TOPIC_DELETE' => $this->config['discord_notification_type_topic_delete'],
116 | 'DN_TOPIC_LOCK' => $this->config['discord_notification_type_topic_lock'],
117 | 'DN_TOPIC_UNLOCK' => $this->config['discord_notification_type_topic_unlock'],
118 |
119 | 'DN_USER_CREATE' => $this->config['discord_notification_type_user_create'],
120 | 'DN_USER_DELETE' => $this->config['discord_notification_type_user_delete'],
121 |
122 | 'U_ACTION' => $this->u_action,
123 | ));
124 | }
125 |
126 | /*
127 | * Called when the user clicks the "Send Test Message" button on the page. Sends the content in the
128 | * Text Message input to Discord.
129 | */
130 | private function process_send_test_message()
131 | {
132 | $webhook_url = $this->request->variable('dn_webhook_url', '');
133 | $test_message = $this->request->variable('dn_test_message', '');
134 |
135 | // Check user inputs before attempting to send the message
136 | if ($test_message == '')
137 | {
138 | trigger_error($this->user->lang('DN_TEST_BAD_MESSAGE') . adm_back_link($this->u_action), E_USER_WARNING);
139 | }
140 | if ($webhook_url == '' || !filter_var($webhook_url, FILTER_VALIDATE_URL))
141 | {
142 | trigger_error($this->user->lang('DN_TEST_BAD_WEBHOOK') . adm_back_link($this->u_action), E_USER_WARNING);
143 | }
144 |
145 | $result = $this->notification_service->force_send_discord_notification($webhook_url, $test_message);
146 | if ($result == true)
147 | {
148 | trigger_error($this->user->lang('DN_TEST_SUCCESS') . adm_back_link($this->u_action));
149 | }
150 | else
151 | {
152 | trigger_error($this->user->lang('DN_TEST_FAILURE') . adm_back_link($this->u_action), E_USER_WARNING);
153 | }
154 | }
155 |
156 | /*
157 | * Handles all error checking and database changes when the user hits the submit button on the ACP page.
158 | */
159 | private function process_form_submit()
160 | {
161 | if (!check_form_key(self::PAGE_FORM_NAME))
162 | {
163 | trigger_error('FORM_INVALID', E_USER_WARNING);
164 | }
165 |
166 | // Get form values for the main settings
167 | $master_enable = $this->request->variable('dn_master_enable', 0);
168 | $webhook_url = $this->request->variable('dn_webhook_url', '');
169 | $preview_length = $this->request->variable('dn_post_preview_length', '');
170 |
171 | // If the master enable is set to on, a webhook URL is required
172 | if ($master_enable == 1 && $webhook_url == '')
173 | {
174 | trigger_error($this->user->lang('DN_MASTER_WEBHOOK_REQUIRED') . adm_back_link($this->u_action), E_USER_WARNING);
175 | }
176 | // Check that the webhook URL is a valid URL string if it is not empty
177 | if ($webhook_url != '' && !filter_var($webhook_url, FILTER_VALIDATE_URL))
178 | {
179 | trigger_error($this->user->lang('DN_WEBHOOK_URL_INVALID') . adm_back_link($this->u_action), E_USER_WARNING);
180 | }
181 | // Verify that the post preview length is a numeric string, convert to an int and check the valid range
182 | if (is_numeric($preview_length) == false)
183 | {
184 | trigger_error($this->user->lang('DN_POST_PREVIEW_INVALID') . adm_back_link($this->u_action), E_USER_WARNING);
185 | }
186 | $preview_length = (int)$preview_length;
187 | if ($preview_length != 0 && ($preview_length < self::MIN_POST_PREVIEW_LENGTH || $preview_length > self::MAX_POST_PREVIEW_LENGTH))
188 | {
189 | trigger_error($this->user->lang('DN_POST_PREVIEW_INVALID') . adm_back_link($this->u_action), E_USER_WARNING);
190 | }
191 |
192 | $this->config->set('discord_notifications_enabled', $master_enable);
193 | $this->config->set('discord_notifications_webhook_url', $webhook_url);
194 | $this->config->set('discord_notifications_post_preview_length', $preview_length);
195 |
196 | $this->config->set('discord_notification_type_post_create', $this->request->variable('dn_post_create', 0));
197 | $this->config->set('discord_notification_type_post_update', $this->request->variable('dn_post_update', 0));
198 | $this->config->set('discord_notification_type_post_delete', $this->request->variable('dn_post_delete', 0));
199 | $this->config->set('discord_notification_type_post_lock', $this->request->variable('dn_post_lock', 0));
200 | $this->config->set('discord_notification_type_post_unlock', $this->request->variable('dn_post_unlock', 0));
201 |
202 | $this->config->set('discord_notification_type_topic_create', $this->request->variable('dn_topic_create', 0));
203 | $this->config->set('discord_notification_type_topic_update', $this->request->variable('dn_topic_update', 0));
204 | $this->config->set('discord_notification_type_topic_delete', $this->request->variable('dn_topic_delete', 0));
205 | $this->config->set('discord_notification_type_topic_lock', $this->request->variable('dn_topic_lock', 0));
206 | $this->config->set('discord_notification_type_topic_unlock', $this->request->variable('dn_topic_unlock', 0));
207 |
208 | $this->config->set('discord_notification_type_user_create', $this->request->variable('dn_user_create', 0));
209 | $this->config->set('discord_notification_type_user_delete', $this->request->variable('dn_user_delete', 0));
210 |
211 | // Set the discord_notifications_enabled in the forum table.
212 | $forum_id_names = array(); // This array will be built up to contain {forum_id} => {input_name}
213 |
214 | // First grab all variables in the submit request and match each against a regex to find the ones that are tied to a forum enabled setting.
215 | $form_inputs = $this->request->variable_names();
216 | foreach ($form_inputs as $input)
217 | {
218 | $matches = array();
219 | $match = preg_match('/^' . self::FORUM_INPUT_PREFIX . '(\d+)$/', $input, $matches);
220 | if ($match === 1)
221 | {
222 | $forum_id_names[$matches[1]] = $input;
223 | }
224 | }
225 |
226 | // Grab all of the values for all of the forum inputs and update the row in the forum table
227 | foreach ($forum_id_names as $id => $input_name)
228 | {
229 | $enabled = (int)$this->request->variable($input_name, 0);
230 | $sql = "UPDATE " . FORUMS_TABLE . " SET discord_notifications_enabled = $enabled WHERE forum_id = $id";
231 | $this->db->sql_query($sql);
232 | // TODO: It would be better to do this update in a single operation instead of once for each input inside this loop
233 | }
234 |
235 | // Log the settings change
236 | $this->log->add('admin', $this->user->data['user_id'], $this->user->ip, 'ACP_DISCORD_NOTIFICATIONS_LOG_UPDATE');
237 | // Destroy any cached discord notification data
238 | $this->cache->destroy('roots_discord_notifications');
239 |
240 | trigger_error($this->user->lang('DN_SETTINGS_SAVED') . adm_back_link($this->u_action));
241 | }
242 |
243 | /**
244 | * Generates the section of the ACP page listing all of the forums, in order, with the radio button option
245 | * that allows the user to enable or disable discord notifications for that forum.
246 | */
247 | private function generate_forum_section()
248 | {
249 | $sql = "SELECT forum_id, forum_type, forum_name, discord_notifications_enabled FROM " . FORUMS_TABLE . " ORDER BY left_id ASC";
250 | $result = $this->db->sql_query($sql);
251 |
252 | while ($row = $this->db->sql_fetchrow($result))
253 | {
254 | // Category forums are displayed for organizational purposes, but have no configuration
255 | if ($row['forum_type'] == FORUM_CAT)
256 | {
257 | $tpl_row = array(
258 | 'S_IS_CAT' => true,
259 | 'FORUM_NAME' => $row['forum_name'],
260 | );
261 | $this->template->assign_block_vars('forumrow', $tpl_row);
262 | }
263 | // Normal forums have a radio input with the value selected based on the value of the discord_notifications_enabled setting
264 | else if ($row['forum_type'] == FORUM_POST)
265 | {
266 | // The labels for all the inputs are constructed based on the forum IDs to make it easy to know which
267 | $tpl_row = array(
268 | 'S_IS_CAT' => false,
269 | 'FORUM_NAME' => $row['forum_name'],
270 | 'FORUM_ID' => self::FORUM_INPUT_PREFIX . $row['forum_id'],
271 | 'FORUM_DN_ENABLED' => $row['discord_notifications_enabled'],
272 | );
273 | $this->template->assign_block_vars('forumrow', $tpl_row);
274 | }
275 | // Other forum types (links) are ignored
276 | }
277 | $this->db->sql_freeresult($result);
278 | }
279 | }
280 |
--------------------------------------------------------------------------------
/adm/style/acp_discord_notifications.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{L_ACP_DISCORD_NOTIFICATIONS}
4 |
5 | {DN_ACP_DESCRIPTION}
6 |
7 |
135 |
136 |
137 |
--------------------------------------------------------------------------------
/build.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
10 |
11 |
12 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
43 |
44 |
45 |
46 |
47 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
95 |
96 |
97 |
98 |
100 |
101 |
102 |
103 |
106 |
107 |
108 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "roots/discordnotifications",
3 | "type": "phpbb-extension",
4 | "description": "Publishes forum event notifications to a Discord server channel",
5 | "homepage": "https://github.com/rootslinux/phpbb-discord-notifications",
6 | "version": "1.0.0",
7 | "keywords": ["phpbb", "extension", "discord"],
8 | "time": "2018-10-14",
9 | "license": "GPL-2.0-only",
10 | "authors": [
11 | {
12 | "name": "Tyler Olsen",
13 | "homepage": "https://github.com/rootslinux",
14 | "role": "Lead Developer"
15 | }
16 | ],
17 | "require": {
18 | "php": ">=5.3.3",
19 | "composer/installers": "~1.0"
20 | },
21 | "extra": {
22 | "display-name": "Discord Notifications",
23 | "soft-require": {
24 | "phpbb/phpbb": ">=3.1.4,<3.2.0@dev"
25 | }
26 | },
27 | "require-dev": {
28 | "phing/phing": "2.4.*"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/config/parameters.yml:
--------------------------------------------------------------------------------
1 | parameters:
2 | roots.discordnotifications.tables.notifications: '%core.table_prefix%notifications'
3 |
--------------------------------------------------------------------------------
/config/services.yml:
--------------------------------------------------------------------------------
1 | imports:
2 | - { resource: parameters.yml }
3 |
4 | services:
5 | roots.discordnotifications.notification_service:
6 | class: roots\discordnotifications\notification_service
7 | arguments:
8 | - '@config'
9 | - '@dbal.conn'
10 |
11 | roots.discordnotifications.listener:
12 | class: roots\discordnotifications\event\notification_event_listener
13 | arguments:
14 | - '@language'
15 | - '@roots.discordnotifications.notification_service'
16 | tags:
17 | - { name: event.listener }
18 |
--------------------------------------------------------------------------------
/event/notification_event_listener.php:
--------------------------------------------------------------------------------
1 | language = $language;
53 | $this->notification_service = $notification_service;
54 |
55 | // Add notifications text from the langauge file
56 | $this->language->add_lang('discord_notification_messages', 'roots/discordnotifications');
57 | }
58 |
59 | /**
60 | * Assigns functions defined in this class to event listeners
61 | * @return array
62 | * @static
63 | * @access public
64 | *
65 | * For description of the event types, refer to https://wiki.phpbb.com/Event_List
66 | */
67 | static public function getSubscribedEvents()
68 | {
69 | return array(
70 | // This event is used for performing actions directly after a post or topic has been submitted.
71 | 'core.submit_post_end' => 'handle_post_submit_action',
72 | // This event is used for performing actions directly after a post or topic has been deleted.
73 | 'core.delete_post_after' => 'handle_post_delete_action',
74 | // Perform additional actions after locking/unlocking posts and topics
75 | 'core.mcp_lock_unlock_after' => 'handle_lock_action',
76 | // Perform additional actions before topic(s) deletion
77 | 'core.delete_topics_before_query' => 'handle_topic_delete_action',
78 | // Event that returns user id, user details and user CPF of newly registered user
79 | 'core.user_add_after' => 'handle_user_add_action',
80 | // This event can be used to modify data after user account's activation
81 | 'core.ucp_activate_after' => 'handle_user_activate_action',
82 | // Perform additional actions after the users have been activated/deactivated
83 | 'core.user_active_flip_after' => 'handle_user_activate_action',
84 | // Event after a user is deleted
85 | 'core.delete_user_after' => 'handle_user_delete_action',
86 | );
87 | }
88 |
89 | // ----------------------------------------------------------------------------
90 | // -------------------------- Event Handler Functions -------------------------
91 | // ----------------------------------------------------------------------------
92 |
93 | /**
94 | * Handles events generated by submitting a post. This could result in a notification of several different types.
95 | * @param \phpbb\event\data $event Event object -- [data, mode, poll, post_visibility, subject, topic_type, update_message, update_search_index, url, username]
96 | *
97 | * The possible notifications that can be generated as a result of these events include:
98 | * - New post created
99 | * - Post updated
100 | * - New topic created
101 | * - Topic updated
102 | */
103 | public function handle_post_submit_action($event)
104 | {
105 | // Check for visibility of the post/topic. We don't send notifications for content that are hidden from normal users.
106 | // Note that there are three visibility settings here. The first is the post visibility when it is generated. For example,
107 | // users may require moderator approval before their posts appear. The other two are the existing visibility status of the topic and post.
108 | if ($event['post_visibility'] === 0 || $event['data']['topic_visibility'] === 0 || $event['data']['post_visibility'] === 0)
109 | {
110 | return;
111 | }
112 |
113 | // Verify that the forum that the post submit action happened in has notifications enabled. If not, we have nothing further to do.
114 | if ($this->notification_service->is_notification_forum_enabled($event['data']['forum_id']) == false)
115 | {
116 | return;
117 | }
118 |
119 | // Build an array of the event data that we may need to pass along to the function that will construct the notification message
120 | $post_data = array(
121 | 'user_id' => $event['data']['poster_id'],
122 | 'user_name' => $event['username'],
123 | 'forum_id' => $event['data']['forum_id'],
124 | 'forum_name' => $event['data']['forum_name'],
125 | 'topic_id' => $event['data']['topic_id'],
126 | 'topic_title' => $event['data']['topic_title'],
127 | 'post_id' => $event['data']['post_id'],
128 | 'post_title' => $event['subject'],
129 | 'edit_user_id' => $event['data']['post_edit_user'],
130 | 'edit_user_name' => $this->language->lang('UNKNOWN_USER'),
131 | 'edit_reason' => $event['data']['post_edit_reason'],
132 | 'content' => $event['data']['message'],
133 | );
134 |
135 | if ($post_data['edit_user_id'] == $post_data['user_id'])
136 | {
137 | $post_data['edit_user_name'] = $post_data['user_name'];
138 | }
139 | else
140 | {
141 | $post_data['edit_user_name'] = $this->language->lang('UNKNOWN_USER');
142 | $edit_name = $this->notification_service->query_user_name($post_data['edit_user_id']);
143 | if ($edit_name != null)
144 | {
145 | $post_data['edit_user_name'] = $edit_name;
146 | }
147 | }
148 |
149 | // Finally, based on the event characteristics we determine which kind of notification we need to send
150 | if ($event['mode'] == 'post') // New topic
151 | {
152 | $this->notify_topic_created($post_data);
153 | }
154 | elseif ($event['mode'] == 'reply' || $event['mode'] == 'quote') // New post
155 | {
156 | $this->notify_post_created($post_data);
157 | }
158 | elseif ($event['mode'] == 'edit' || $event['mode'] == 'edit_topic' || $event['mode'] == 'edit_first_post' || $event['mode'] == 'edit_last_post') // Edit existing post
159 | {
160 | // If the post that was edited is the first one in the topic, we consider this a topic update event.
161 | if ($event['data']['post_id'] == $event['data']['topic_first_post_id'])
162 | {
163 | $this->notify_topic_updated($post_data);
164 | }
165 | // Otherwise we treat this as a post update event.
166 | else
167 | {
168 | $this->notify_post_updated($post_data);
169 | }
170 | }
171 | }
172 |
173 | /**
174 | * Handles events generated by deleting a post.
175 | * @param \phpbb\event\data $event Event object -- [data, forum_id, is_soft, next_post_id, post_id, post_mode, softdelete_reason, topic_id]
176 | */
177 | public function handle_post_delete_action($event)
178 | {
179 | // Check for visibility of the post/topic. We don't send notifications for content that is hidden from normal users.
180 | if ($event['data']['topic_visibility'] == 0 || $event['data']['post_visibility'] == 0)
181 | {
182 | return;
183 | }
184 |
185 | // Verify that the forum that the post delete action happened in has notifications enabled. If not, we have nothing further to do.
186 | if ($this->notification_service->is_notification_forum_enabled($event['forum_id']) == false)
187 | {
188 | return;
189 | }
190 |
191 | // Build an array of the event data that we may need to pass along to the function that will construct the notification message.
192 | // Note that unfortunately, the event data does not give us any information indicating which user deleted the post.
193 | $post_data = array(
194 | 'user_id' => $event['data']['poster_id'],
195 | 'user_name' => $this->language->lang('UNKNOWN_USER'),
196 | 'forum_id' => $event['forum_id'],
197 | 'forum_name' => $this->language->lang('UNKNOWN_FORUM'),
198 | 'topic_id' => $event['topic_id'],
199 | 'topic_title' => $this->language->lang('UNKNOWN_TOPIC'),
200 | 'post_id' => $event['post_id'],
201 | 'delete_reason' => $event['softdelete_reason'],
202 | );
203 |
204 | // Fetch the forum name, topic title, and user name using the respective IDs.
205 | $forum_name = $this->notification_service->query_forum_name($post_data['forum_id']);
206 | if ($forum_name != null)
207 | {
208 | $post_data['forum_name'] = $forum_name;
209 | }
210 | $topic_title = $this->notification_service->query_topic_title($post_data['topic_id']);
211 | if ($topic_title != null)
212 | {
213 | $post_data['topic_title'] = $topic_title;
214 | }
215 | $user_name = $this->notification_service->query_user_name($post_data['user_id']);
216 | if ($user_name != null)
217 | {
218 | $post_data['user_name'] = $user_name;
219 | }
220 |
221 | $this->notify_post_deleted($post_data);
222 | }
223 |
224 | /**
225 | * Handles events generated by deleting a topic.
226 | * @param \phpbb\event\data $event Event object -- [table_ary, topic_ids]
227 | */
228 | public function handle_topic_delete_action($event)
229 | {
230 | // Notification messages can get complicated when more than one topic is deleted in a transaction. We choose to not generate
231 | // a notification in this case.
232 | if (count($event['topic_ids']) > 1)
233 | {
234 | return;
235 | }
236 |
237 | // Unfortunately the only useful data we get from this event is the topic ID. We have to run a custom query to retrieve the
238 | // rest of the data that we are interested in.
239 | $topic_id = (int)array_pop($event['topic_ids']);
240 | $query_data = $this->notification_service->query_topic_details($topic_id);
241 |
242 | // Check for visibility of the topic. We don't send notifications for content that is hidden from normal users.
243 | if ($query_data['topic_visibility'] == 0)
244 | {
245 | return;
246 | }
247 |
248 | // Verify that the forum that the topic delete action happened in has notifications enabled. If not, we have nothing further to do.
249 | if ($this->notification_service->is_notification_forum_enabled($query_data['forum_id']) == false)
250 | {
251 | return;
252 | }
253 |
254 | // Copy over the data necessary to generate the notification into a new array
255 | $delete_data = array();
256 | $delete_data['forum_id'] = $query_data['forum_id'];
257 | $delete_data['forum_name'] = $query_data['forum_name'];
258 | $delete_data['topic_title'] = $query_data['topic_title'];
259 | $delete_data['topic_post_count'] = $query_data['topic_posts_approved'];
260 | $delete_data['user_id'] = $query_data['topic_poster'];
261 | $delete_data['user_name'] = $query_data['topic_first_poster_name'];
262 |
263 | $this->notify_topic_deleted($delete_data);
264 | }
265 |
266 | /**
267 | * Handles events generated by changing the lock status on a topic or post.
268 | * @param \phpbb\event\data $event Event object -- [action, data, ids]
269 | *
270 | * The possible notifications that can be generated as a result of these events include:
271 | * - Post locked
272 | * - Post unlocked
273 | * - Topic locked
274 | * - Topic unlocked
275 | */
276 | public function handle_lock_action($event)
277 | {
278 | // Notification messages can get complicated if we have multiple topics or posts that are locked/unlocked in a single transaction.
279 | // Presently we choose not to take any action on such operations.
280 | if (count($event['ids']) > 1)
281 | {
282 | return;
283 | }
284 |
285 | // Get the ID needed to access $event['data'], then extract all relevant data from the event that we need to generate the notification
286 | $id = array_pop($event['ids']);
287 |
288 | $lock_data = array();
289 | $lock_data['forum_id'] = $event['data'][$id]['forum_id'];
290 | $lock_data['forum_name'] = $event['data'][$id]['forum_name'];
291 | $lock_data['post_id'] = $event['data'][$id]['post_id'];
292 | $lock_data['post_subject'] = $event['data'][$id]['post_subject'];
293 | $lock_data['topic_id'] = $event['data'][$id]['topic_id'];
294 | $lock_data['topic_title'] = $event['data'][$id]['topic_title'];
295 | // Two sets of user data captured: one for the post (if applicable) and one for the user that started the topic
296 | $lock_data['post_user_id'] = $event['data'][$id]['poster_id'];
297 | $lock_data['post_user_name'] = $event['data'][$id]['username'];
298 | $lock_data['topic_user_id'] = $event['data'][$id]['topic_poster'];
299 | $lock_data['topic_user_name'] = $event['data'][$id]['topic_first_poster_name'];
300 |
301 | // If the forum the post was made in does not have notifications enabled or the topic/poar is not visible, do nothing more.
302 | $topic_visibile = $event['data'][$id]['topic_visibility'] == 1 ? true : false;
303 | if ($this->notification_service->is_notification_forum_enabled($lock_data['forum_id']) == false || $topic_visibile == false)
304 | {
305 | return;
306 | }
307 |
308 | // The action determines whether the action was a lock or unlock event and thus which notification to generate
309 | switch ($event['action'])
310 | {
311 | case 'lock_post':
312 | $this->notify_post_locked($lock_data);
313 | break;
314 | case 'unlock_post':
315 | $this->notify_post_unlocked($lock_data);
316 | break;
317 | case 'lock':
318 | $this->notify_topic_locked($lock_data);
319 | break;
320 | case 'unlock':
321 | $this->notify_topic_unlocked($lock_data);
322 | break;
323 | }
324 | }
325 |
326 | /**
327 | * Handles events generated by the creation of a new user account.
328 | * @param \phpbb\event\data $event Event object -- [cp_data, user_id, user_row]
329 | *
330 | * If the user account that was created is initially inactive or a bot, we don't generate a user created
331 | * notification. Instead, we wait until the user activates their account.
332 | */
333 | public function handle_user_add_action($event)
334 | {
335 | // Only generate a notification if the user starts off as a normal, activated user.
336 | if ($event['user_row']['user_type'] != USER_NORMAL)
337 | {
338 | return;
339 | }
340 |
341 | // Notifications are only generated if user activation is disabled and this is a normal user
342 | $user_data['user_id'] = $event['user_id'];
343 | $user_data['user_name'] = $event['user_row']['username'];
344 |
345 | $this->notify_user_created($user_data);
346 | }
347 |
348 | /**
349 | * Handles events generated by the activation or deactivation of a user account.
350 | * @param \phpbb\event\data $event Event object -- [message, user_row] -or- [activated, deactivated, mode, reason, sql_statements, user_id_ary]
351 | *
352 | * There are two different types of events that can trigger this function to be called, and each one provides different types of event data.
353 | * The first type of event, "core.ucp_activate_after" occurs when a user activates their own account. The second event, "core.user_active_flip_after"
354 | * occurs when an adminstrator either activates or deactivates one or more user accounts. We look at the event data to figure out which type
355 | * of event occurred and from there pass along the appropriate data to the notify_user_created() function.
356 |
357 | */
358 | public function handle_user_activate_action($event)
359 | {
360 | $user_data = array();
361 | if ($event['user_id'])
362 | {
363 | $user_data['user_id'] = $event['user_id'];
364 | $user_data['user_name'] = $event['user_row']['username'];
365 | }
366 | elseif ($event['activated'] == 1)
367 | {
368 | $user_data['user_id'] = array_pop($event['user_id_ary']);
369 | $user_name = $this->notification_service->query_user_name($user_data['user_id']);
370 | $user_data['user_name'] = ($user_name != null) ? $user_name : $this->language->lang('UNKNOWN_USER');
371 | }
372 | // Ignore deactivated user case
373 | else
374 | {
375 | return;
376 | }
377 |
378 | $this->notify_user_created($user_data);
379 | }
380 |
381 | /**
382 | * Handles events generated by the deletion of one or more user account.
383 | * @param \phpbb\event\data $event Event object -- [mode, retain_username, user_ids, user_rows]
384 | */
385 | public function handle_user_delete_action($event)
386 | {
387 | // Extract the IDs and names of all deleted users to pass along in an array of (id => name)
388 | $user_data = array();
389 | foreach ($event['user_ids'] as $id)
390 | {
391 | $user_data[$id] = $event['user_rows'][$id]['username'];
392 | }
393 |
394 | $this->notify_users_deleted($user_data);
395 | }
396 |
397 | // ----------------------------------------------------------------------------
398 | // --------------------- Notification Generation Functions --------------------
399 | // ----------------------------------------------------------------------------
400 |
401 | /**
402 | * Sends a notification to Discord when a new post is created.
403 | * @param $data Array of attributes for the new post
404 | */
405 | private function notify_post_created($data)
406 | {
407 | // Constant properties for this notification type
408 | $notification_type_config_name = 'discord_notification_type_post_create';
409 | $color = self::COLOR_BRIGHT_GREEN;
410 | $emoji = self::EMOJI_CREATE;
411 |
412 | // Verify that this notification type is enabled. If not, we have nothing further to do.
413 | if ($this->notification_service->is_notification_type_enabled($notification_type_config_name) == false) {
414 | return;
415 | }
416 |
417 | // Construct the notification message using the post data
418 | $user_link = $this->generate_user_link($data['user_id'], $data['user_name']);
419 | $post_link = $this->generate_post_link($data['topic_id'], $data['post_id'], $this->language->lang('POST'));
420 | $topic_link = $this->generate_topic_link($data['topic_id'], $data['topic_title']);
421 | $forum_link = $this->generate_forum_link($data['forum_id'], $data['forum_name']);
422 | $message = sprintf($this->language->lang('CREATE_POST'),
423 | $emoji, $user_link, $post_link, $topic_link, $forum_link
424 | );
425 |
426 | // Generate a post preview if necessary
427 | $footer = $this->generate_footer_text($this->language->lang('PREVIEW'), $data['content']);
428 |
429 | $this->notification_service->send_discord_notification($color, $message, $footer);
430 | }
431 |
432 | /**
433 | * Sends a notification to Discord when a post is updated.
434 | * @param $data Array of attributes for the updated post
435 | */
436 | private function notify_post_updated($data)
437 | {
438 | // Constant properties for this notification type
439 | $notification_type_config_name = 'discord_notification_type_post_update';
440 | $color = self::COLOR_BRIGHT_BLUE;
441 | $emoji = self::EMOJI_UPDATE;
442 |
443 | // Verify that this notification type is enabled. If not, we have nothing further to do.
444 | if ($this->notification_service->is_notification_type_enabled($notification_type_config_name) == false) {
445 | return;
446 | }
447 |
448 | // Construct the notification message using the post data
449 | $user_link = $this->generate_user_link($data['user_id'], $data['user_name']);
450 | $post_link = $this->generate_post_link($data['topic_id'], $data['post_id'], $this->language->lang('POST'));
451 | $topic_link = $this->generate_topic_link($data['topic_id'], $data['topic_title']);
452 | $forum_link = $this->generate_forum_link($data['forum_id'], $data['forum_name']);
453 |
454 | // The notification is slightly different depending on whether the user edited their own post, or another user made the edit.
455 | $message = '';
456 | if ($data['user_id'] == $data['edit_user_id'])
457 | {
458 | $message = sprintf($this->language->lang('UPDATE_POST_SELF'),
459 | $emoji, $user_link, $post_link, $topic_link, $forum_link
460 | );
461 | }
462 | else
463 | {
464 | $edit_user_link = $this->generate_user_link($data['edit_user_id'], $data['edit_user_name']);
465 | $message = sprintf($this->language->lang('UPDATE_POST_OTHER'),
466 | $emoji, $edit_user_link, $post_link, $user_link, $topic_link, $forum_link
467 | );
468 | }
469 |
470 | // If we allow previews and an edit reason was given, add that information in the footer
471 | $footer = null;
472 | if (isset($data['edit_reason']) && $data['edit_reason'] !== '')
473 | {
474 | $footer = $this->generate_footer_text($this->language->lang('REASON'), $data['edit_reason']);
475 | }
476 |
477 | $this->notification_service->send_discord_notification($color, $message, $footer);
478 | }
479 |
480 | /**
481 | * Sends a notification to Discord when a post is deleted.
482 | * @param $data Array of attributes for the deleted post
483 | */
484 | private function notify_post_deleted($data)
485 | {
486 | // Constant properties for this notification type
487 | $notification_type_config_name = 'discord_notification_type_post_delete';
488 | $color = self::COLOR_BRIGHT_RED;
489 | $emoji = self::EMOJI_DELETE;
490 |
491 | // Verify that this notification type is enabled. If not, we have nothing further to do.
492 | if ($this->notification_service->is_notification_type_enabled($notification_type_config_name) == false) {
493 | return;
494 | }
495 |
496 | // Construct the notification message using the post data.
497 | $user_name = $data['user_name'] !== '' ? $data['user_name'] : $this->language->lang('UNKNOWN_USER');
498 | $user_link = $this->generate_user_link($data['user_id'], $user_name);
499 | $topic_title = $data['topic_title'] !== '' ? $data['topic_title'] : $this->language->lang('UNKNOWN_TOPIC');
500 | $topic_link = $this->generate_topic_link($data['topic_id'], $topic_title);
501 | $forum_name = $data['forum_name'] !== '' ? $data['forum_name'] : $this->language->lang('UNKNOWN_FORUM');
502 | $forum_link = $this->generate_forum_link($data['forum_id'], $forum_name);
503 |
504 | $message = sprintf($this->language->lang('DELETE_POST'),
505 | $emoji, $user_link, $topic_link, $forum_link
506 | );
507 |
508 | // If there was a reason specified for the delete, include that in the message footer.
509 | $footer = null;
510 | if (is_string($data['delete_reason']) && $data['delete_reason'] !== '')
511 | {
512 | $footer = $this->generate_footer_text($this->language->lang('REASON'), $data['delete_reason']);
513 | }
514 |
515 | $this->notification_service->send_discord_notification($color, $message, $footer);
516 | }
517 |
518 | /**
519 | * Sends a notification to Discord when a post is locked.
520 | * @param $data Array of attributes for the locked post
521 | */
522 | private function notify_post_locked($data)
523 | {
524 | // Constant properties for this notification type
525 | $notification_type_config_name = 'discord_notification_type_post_lock';
526 | $color = self::COLOR_BRIGHT_ORANGE;
527 | $emoji = self::EMOJI_LOCK;
528 |
529 | // Verify that this notification type is enabled. If not, we have nothing further to do.
530 | if ($this->notification_service->is_notification_type_enabled($notification_type_config_name) == false) {
531 | return;
532 | }
533 |
534 | // Construct the notification message using the argument data
535 | $user_link = $this->generate_user_link($data['post_user_id'], $data['post_user_name']);
536 | $forum_link = $this->generate_forum_link($data['forum_id'], $data['forum_name']);
537 | $topic_link = $this->generate_topic_link($data['topic_id'], $data['topic_title']);
538 | $post_link = $this->generate_post_link($data['topic_id'], $data['post_id'], $this->language->lang('POST'));
539 | $message = sprintf($this->language->lang('LOCK_POST'),
540 | $emoji, $post_link, $user_link, $topic_link, $forum_link
541 | );
542 |
543 | $this->notification_service->send_discord_notification($color, $message);
544 | }
545 |
546 | /**
547 | * Sends a notification to Discord when a post is unlocked.
548 | * @param $data Array of attributes for the unlocked post
549 | */
550 | private function notify_post_unlocked($data)
551 | {
552 | // Constant properties for this notification type
553 | $notification_type_config_name = 'discord_notification_type_post_unlock';
554 | $color = self::COLOR_BRIGHT_ORANGE;
555 | $emoji = self::EMOJI_UNLOCK;
556 |
557 | // Verify that this notification type is enabled. If not, we have nothing further to do.
558 | if ($this->notification_service->is_notification_type_enabled($notification_type_config_name) == false) {
559 | return;
560 | }
561 |
562 | // Construct the notification message using the argument data
563 | $user_link = $this->generate_user_link($data['post_user_id'], $data['post_user_name']);
564 | $forum_link = $this->generate_forum_link($data['forum_id'], $data['forum_name']);
565 | $topic_link = $this->generate_topic_link($data['topic_id'], $data['topic_title']);
566 | $post_link = $this->generate_post_link($data['topic_id'], $data['post_id'], $this->language->lang('POST'));
567 | $message = sprintf($this->language->lang('UNLOCK_POST'),
568 | $emoji, $post_link, $user_link, $topic_link, $forum_link
569 | );
570 |
571 | $this->notification_service->send_discord_notification($color, $message);
572 | }
573 |
574 | /**
575 | * Sends a notification to Discord when a new topic is created.
576 | * @param $data Array of attributes for the new topic
577 | */
578 | private function notify_topic_created($data)
579 | {
580 | // Constant properties for this notification type
581 | $notification_type_config_name = 'discord_notification_type_topic_create';
582 | $color = self::COLOR_BRIGHT_GREEN;
583 | $emoji = self::EMOJI_CREATE;
584 |
585 | // Verify that this notification type is enabled. If not, we have nothing further to do.
586 | if ($this->notification_service->is_notification_type_enabled($notification_type_config_name) == false) {
587 | return;
588 | }
589 |
590 | // Construct the notification message using the argument data
591 | $user_link = $this->generate_user_link($data['user_id'], $data['user_name']);
592 | $forum_link = $this->generate_forum_link($data['forum_id'], $data['forum_name']);
593 | $topic_link = $this->generate_topic_link($data['topic_id'], $data['topic_title']);
594 | $message = sprintf($this->language->lang('CREATE_TOPIC'),
595 | $emoji, $user_link, $topic_link, $forum_link
596 | );
597 |
598 | // Generates a topic preview if necessary
599 | $footer = $this->generate_footer_text($this->language->lang('PREVIEW'), $data['content']);
600 |
601 | $this->notification_service->send_discord_notification($color, $message, $footer);
602 | }
603 |
604 | /**
605 | * Sends a notification to Discord when a topic is updated.
606 | * @param $data Array of attributes for the updated topic
607 | */
608 | private function notify_topic_updated($data)
609 | {
610 | // Constant properties for this notification type
611 | $notification_type_config_name = 'discord_notification_type_topic_update';
612 | $color = self::COLOR_BRIGHT_BLUE;
613 | $emoji = self::EMOJI_UPDATE;
614 |
615 | // Verify that this notification type is enabled. If not, we have nothing further to do.
616 | if ($this->notification_service->is_notification_type_enabled($notification_type_config_name) == false) {
617 | return;
618 | }
619 |
620 | // Construct the notification message using the topic data
621 | $user_link = $this->generate_user_link($data['user_id'], $data['user_name']);
622 | $topic_link = $this->generate_topic_link($data['topic_id'], $data['topic_title']);
623 | $forum_link = $this->generate_forum_link($data['forum_id'], $data['forum_name']);
624 |
625 | // The notification is slightly different depending on whether the user edited their own topic, or another user made the edit
626 | $message = '';
627 | if ($data['user_id'] == $data['edit_user_id'])
628 | {
629 | $message = sprintf($this->language->lang('UPDATE_TOPIC_SELF'),
630 | $emoji, $user_link, $topic_link, $forum_link
631 | );
632 | }
633 | else
634 | {
635 | $edit_user_link = $this->generate_user_link($data['edit_user_id'], $data['edit_user_name']);
636 | $message = sprintf($this->language->lang('UPDATE_TOPIC_OTHER'),
637 | $emoji, $edit_user_link, $topic_link, $user_link, $forum_link
638 | );
639 | }
640 |
641 | // If an edit reason was given, add that information in the footer
642 | $footer = null;
643 | if (isset($data['edit_reason']) && $data['edit_reason'] !== '')
644 | {
645 | $footer = $this->generate_footer_text($this->language->lang('REASON'), $data['edit_reason']);
646 | }
647 |
648 | $this->notification_service->send_discord_notification($color, $message, $footer);
649 | }
650 |
651 | /**
652 | * Sends a notification to Discord when a topic is deleted.
653 | * @param $data Array of attributes for the deleted topic
654 | */
655 | private function notify_topic_deleted($data)
656 | {
657 | // Constant properties for this notification type
658 | $notification_type_config_name = 'discord_notification_type_topic_delete';
659 | $color = self::COLOR_BRIGHT_RED;
660 | $emoji = self::EMOJI_DELETE;
661 |
662 | // Verify that this notification type is enabled. If not, we have nothing further to do.
663 | if ($this->notification_service->is_notification_type_enabled($notification_type_config_name) == false) {
664 | return;
665 | }
666 |
667 | // Construct the notification message using the argument data
668 | $user_link = $this->generate_user_link($data['user_id'], $data['user_name']);
669 | $forum_link = $this->generate_forum_link($data['forum_id'], $data['forum_name']);
670 | $message = sprintf($this->language->lang('DELETE_TOPIC'),
671 | $emoji, $user_link, $data['topic_title'], $data['topic_post_count'], $forum_link
672 | );
673 |
674 | $this->notification_service->send_discord_notification($color, $message);
675 | }
676 |
677 | /**
678 | * Sends a notification to Discord when a topic is locked.
679 | * @param $data Array of attributes for the locked topic
680 | */
681 | private function notify_topic_locked($data)
682 | {
683 | // Constant properties for this notification type
684 | $notification_type_config_name = 'discord_notification_type_topic_lock';
685 | $color = self::COLOR_BRIGHT_ORANGE;
686 | $emoji = self::EMOJI_LOCK;
687 |
688 | // Verify that this notification type is enabled. If not, we have nothing further to do.
689 | if ($this->notification_service->is_notification_type_enabled($notification_type_config_name) == false) {
690 | return;
691 | }
692 |
693 | // Construct the notification message using the argument data
694 | $user_link = $this->generate_user_link($data['topic_user_id'], $data['topic_user_name']);
695 | $forum_link = $this->generate_forum_link($data['forum_id'], $data['forum_name']);
696 | $topic_link = $this->generate_topic_link($data['topic_id'], $data['topic_title']);
697 | $message = sprintf($this->language->lang('LOCK_TOPIC'),
698 | $emoji, $topic_link, $forum_link, $user_link
699 | );
700 |
701 | $this->notification_service->send_discord_notification($color, $message);
702 | }
703 |
704 | /**
705 | * Sends a notification to Discord when a topic is unlocked.
706 | * @param $data Array of attributes for the unlocked topic
707 | */
708 | private function notify_topic_unlocked($data)
709 | {
710 | // Constant properties for this notification type
711 | $notification_type_config_name = 'discord_notification_type_topic_unlock';
712 | $color = self::COLOR_BRIGHT_ORANGE;
713 | $emoji = self::EMOJI_UNLOCK;
714 |
715 | // Verify that this notification type is enabled. If not, we have nothing further to do.
716 | if ($this->notification_service->is_notification_type_enabled($notification_type_config_name) == false) {
717 | return;
718 | }
719 |
720 | // Construct the notification message using the argument data
721 | $user_link = $this->generate_user_link($data['topic_user_id'], $data['topic_user_name']);
722 | $forum_link = $this->generate_forum_link($data['forum_id'], $data['forum_name']);
723 | $topic_link = $this->generate_topic_link($data['topic_id'], $data['topic_title']);
724 | $message = sprintf($this->language->lang('UNLOCK_TOPIC'),
725 | $emoji, $topic_link, $forum_link, $user_link
726 | );
727 |
728 | $this->notification_service->send_discord_notification($color, $message);
729 | }
730 |
731 | /**
732 | * Sends a notification to Discord when a new user account is created.
733 | * @param $data Array of attributes for the new user
734 | *
735 | * Notification details include the user name and a link to the user's profile page.
736 | */
737 | private function notify_user_created($data)
738 | {
739 | // Constant properties for this notification type
740 | $notification_type_config_name = 'discord_notification_type_user_create';
741 | $color = self::COLOR_BRIGHT_PURPLE;
742 | $emoji = self::EMOJI_USER;
743 |
744 | // Verify that this notification type is enabled. If not, we have nothing further to do.
745 | if ($this->notification_service->is_notification_type_enabled($notification_type_config_name) == false) {
746 | return;
747 | }
748 |
749 | // Construct the notification message using the argument data.
750 | $user_link = $this->generate_user_link($data['user_id'], $data['user_name']);
751 | $message = sprintf($this->language->lang('CREATE_USER'),
752 | $emoji, $user_link
753 | );
754 |
755 | $this->notification_service->send_discord_notification($color, $message);
756 | }
757 |
758 | /**
759 | * Sends a notification to Discord when one or more user accounts are deleted.
760 | * @param $data Array of attributes for the deleted users (id => username)
761 | *
762 | * The notification lists the usernames of those deleted but provides no links or additional information such as deletion reason.
763 | */
764 | private function notify_users_deleted($data)
765 | {
766 | // Constant properties for this notification type
767 | $notification_type_config_name = 'discord_notification_type_user_delete';
768 | $color = self::COLOR_BRIGHT_PURPLE;
769 | $emoji = self::EMOJI_USER;
770 |
771 | // Verify that this notification type is enabled. If not, we have nothing further to do.
772 | if ($this->notification_service->is_notification_type_enabled($notification_type_config_name) == false) {
773 | return;
774 | }
775 |
776 | // Construct the notification message using the argument data.
777 | $message = '';
778 | // The message format is slightly different depending on how many users were deleted.
779 | if (count($data) == 1)
780 | {
781 | $user_name = array_pop($data);
782 | $message = sprintf($this->language->lang('DELETE_USER'),
783 | $emoji, $user_name
784 | );
785 | }
786 | else {
787 | $deleted_users_text = '';
788 | $and = $this->language->lang('AND');
789 | $comma = $this->language->lang('CONJ');
790 | if (count($data) == 2)
791 | {
792 | $deleted_users_text = array_pop($data) . " $and " . array_pop($data);
793 | }
794 | elseif (count($data) == 3)
795 | {
796 | $deleted_users_text = array_pop($data) . "$comma " . array_pop($data) . "$comma $and " . array_pop($data);
797 | }
798 | // If more than three users were deleted, we display three user names and then the number of additional deletions
799 | elseif (count($data) > 3)
800 | {
801 | $deleted_users_text = array_pop($data) . "$comma " . array_pop($data) . "$comma " . array_pop($data) . "$comma $and " . count($data);
802 | // Singular vs plural case check
803 | if (count($data) == 1)
804 | {
805 | $deleted_users_text .= " " . $this->language->lang('OTHER');
806 | }
807 | else
808 | {
809 | $deleted_users_text .= " " . $this->language->lang('OTHERS');
810 | }
811 | }
812 |
813 | $message = sprintf($this->language->lang('DELETE_MULTI_USER'),
814 | $emoji, $deleted_users_text
815 | );
816 | }
817 |
818 | $this->notification_service->send_discord_notification($color, $message);
819 | }
820 |
821 | // ----------------------------------------------------------------------------
822 | // ----------------------------- Helper Functions -----------------------------
823 | // ----------------------------------------------------------------------------
824 |
825 | /**
826 | * The Discord webhook api does not accept urlencoded text. This function replaces problematic characters.
827 | * @param $url
828 | * @return Formatted URL text
829 | */
830 | private function reformat_link_url($url)
831 | {
832 | $url = str_replace(" ", "%20", $url);
833 | $url = str_replace("(", "%28", $url);
834 | $url = str_replace(")", "%29", $url);
835 | return $url;
836 | }
837 |
838 | /**
839 | * Discord link text must be surrounded by []. This function replaces problematic characters
840 | * @param $text Text link
841 | * @return Formatted link-safe text
842 | */
843 | private function reformat_link_text($text)
844 | {
845 | $text = str_replace("[", "(", $text);
846 | $text = str_replace("]", ")", $text);
847 | return $text;
848 | }
849 |
850 | /**
851 | * Removes all HTML and BBcode formatting tags from a string
852 | * @param $text Text link
853 | * @return Formatted text
854 | *
855 | * Note that there is some risk here of the text not coming out exactly as we may like. The user text
856 | * may include characters that look like pseudo-HTML and get picked up by the regex used.
857 | */
858 | private function remove_formatting($text)
859 | {
860 | $text = strip_tags($text);
861 | $text = preg_replace('|[[\/\!]*?[^\[\]]*?]|si', '', $text);
862 | return $text;
863 | }
864 |
865 | /**
866 | * Given the ID of a forum, returns text that contains a link to view the forum
867 | * @param $topic_id The ID of the topic
868 | * @param $post_id The ID of the post
869 | * @param $text The text to display for the post link
870 | * @return Text formatted in the notation that Discord would interpret.
871 | */
872 | private function generate_forum_link($forum_id, $text)
873 | {
874 | $url = generate_board_url() . '/viewforum.php?f=' . $forum_id;
875 | $url = $this->reformat_link_url($url);
876 | $text = $this->reformat_link_text($text);
877 | return sprintf('[%s](%s)', $text, $url);
878 | }
879 |
880 | /**
881 | * Given the ID of a valid post, returns text that contains the post title with a link to the post.
882 | * @param $topic_id The ID of the topic
883 | * @param $post_id The ID of the post
884 | * @param $text The text to display for the post link
885 | * @return Text formatted in the notation that Discord would interpret.
886 | */
887 | private function generate_post_link($topic_id, $post_id, $text)
888 | {
889 | $url = generate_board_url() . '/viewtopic.php?t=' . $topic_id . '&p=' . $post_id . '#p' . $post_id;
890 | $url = $this->reformat_link_url($url);
891 | $text = $this->reformat_link_text($text);
892 | return sprintf('[%s](%s)', $text, $url);
893 | }
894 |
895 | /**
896 | * Given the ID of a valid topic, returns text that contains the topic title with a link to the topic.
897 | * @param $topic_id The ID of the topic
898 | * @param $text The text to display for the topic link
899 | * @return Text formatted in the notation that Discord would interpret.
900 | */
901 | private function generate_topic_link($topic_id, $text)
902 | {
903 | $url = generate_board_url() . '/viewtopic.php?t=' . $topic_id;
904 | $url = $this->reformat_link_url($url);
905 | $text = $this->reformat_link_text($text);
906 | return sprintf('[%s](%s)', $text, $url);
907 | }
908 |
909 | /**
910 | * Given the ID of a valid user, returns text that contains the user name with a link to their user profile.
911 | * @param $user_id The ID of the user
912 | * @param $text The text to display for the user link
913 | * @return Text formatted in the notation that Discord would interpret.
914 | */
915 | private function generate_user_link($user_id, $text)
916 | {
917 | $url = generate_board_url() . '/memberlist.php?mode=viewprofile&u=' . $user_id;
918 | $url = $this->reformat_link_url($url);
919 | $text = $this->reformat_link_text($text);
920 | return sprintf('[%s](%s)', $text, $url);
921 | }
922 |
923 | /**
924 | * Formats and prepares text to be placed in the footer of a notification message.
925 | * @param $prepend_text Text to add before the content
926 | * @param $content The raw text to place in the footer
927 | * @return A string meeting the configuration requirements for a footer, or NULL if a footer should not be generated at all.
928 | */
929 | private function generate_footer_text($prepend_text, $content)
930 | {
931 | $preview_length = $this->notification_service->get_post_preview_length();
932 | if ($preview_length == 0)
933 | {
934 | return null;
935 | }
936 |
937 | $footer = $this->remove_formatting($content);
938 | // Truncate the content if it is too long and add '...' for the last three characters. The preview length will
939 | // always be at least 10 characters so we don't need to worry about really short strings.
940 | if (strlen($footer) > $preview_length)
941 | {
942 | $footer = substr($footer, 0, $preview_length - 3) . '...';
943 | }
944 |
945 | // Prepend text to the footer so that it's clear what content we are sharing in the footer
946 | $footer = $prepend_text . $footer;
947 | return $footer;
948 | }
949 | }
950 |
--------------------------------------------------------------------------------
/language/en/acp_discord_notifications.php:
--------------------------------------------------------------------------------
1 | 'These settings allow for various forum events to send notification messages to a Discord server.',
24 |
25 | 'DN_MAIN_SETTINGS' => 'Main Settings',
26 | 'DN_MASTER_ENABLE' => 'Enable Discord Notifications',
27 | 'DN_WEBHOOK_URL' => 'Discord Webhook URL',
28 | 'DN_WEBHOOK_DESCRIPTION' => 'The URL of the webhook generated by the Discord server. See this article to learn how to create a new webhook.',
29 | 'DN_POST_PREVIEW_LENGTH' => 'Post Preview Length',
30 | 'DN_POST_PREVIEW_DESCRIPTION' => 'Specify the number of characters to display in a post preview (10 - 2000). A zero value disables post preview.',
31 | 'DN_TEST_MESSAGE' => 'Test Message',
32 | 'DN_TEST_MESSAGE_TEXT' => 'This is a test: Hello Discord!',
33 | 'DN_TEST_DESCRIPTION' => 'A message to send to Discord to verify that the connection with phpBB is functioning.',
34 | 'DN_SEND_TEST' => 'Send Test Message',
35 | 'DN_SEND_TEST_DESCRIPTION' => 'Sends the contents of the test message to the Discord webhook. Use this to verify that your webhook is properly functioning.',
36 |
37 | 'DN_TYPE_SETTINGS' => 'Notification Types',
38 | 'DN_TYPE_DESCRIPTION' => 'Select which types of notifications should generate messages that get sent to Discord',
39 | 'DN_POST_CREATE' => 'Post created',
40 | 'DN_POST_UPDATE' => 'Post updated',
41 | 'DN_POST_DELETE' => 'Post deleted',
42 | 'DN_POST_LOCK' => 'Post locked',
43 | 'DN_POST_UNLOCK' => 'Post unlocked',
44 | 'DN_TOPIC_CREATE' => 'Topic created',
45 | 'DN_TOPIC_UPDATE' => 'Topic updated',
46 | 'DN_TOPIC_DELETE' => 'Topic deleted',
47 | 'DN_TOPIC_LOCK' => 'Topic locked',
48 | 'DN_TOPIC_UNLOCK' => 'Topic unlocked',
49 | 'DN_USER_CREATE' => 'User created',
50 | 'DN_USER_DELETE' => 'User deleted',
51 |
52 | 'DN_FORUM_SETTINGS' => 'Notification Forums',
53 | 'DN_FORUM_DESCRIPTION' => 'Select which forums should generate notifications for forum-specific events, such as those related to posts and topics',
54 |
55 | // Messages that appear after a user tries to send a test message
56 | 'DN_TEST_SUCCESS' => 'Success! Check your Discord server and you should see the test message displayed there.',
57 | 'DN_TEST_FAILURE' => 'There was a problem with sending the test message. Check your webhook URL and verify your server meets the extension requirements.',
58 | 'DN_TEST_BAD_MESSAGE' => 'Please enter at least one character for the test message',
59 | 'DN_TEST_BAD_WEBHOOK' => 'The text entered for the webhook URL is not a valid URL. Please check this setting and try again.',
60 |
61 | // Success/Failure messages that can be generated once the user saves
62 | 'DN_SETTINGS_SAVED' => 'Discord Notification settings modified successfully.',
63 | 'DN_MASTER_WEBHOOK_REQUIRED' => 'A valid Discord webhook URL is required to enable notifications.',
64 | 'DN_WEBHOOK_URL_INVALID' => 'Discord webhook URL must be a full and valid URL.',
65 | 'DN_POST_PREVIEW_INVALID' => 'Post preview length must be a number between 10 and 2000, or 0 to disable previews.',
66 | ));
67 |
--------------------------------------------------------------------------------
/language/en/discord_notification_messages.php:
--------------------------------------------------------------------------------
1 | '%s %s created a new %s in the topic %s located in the forum %s', // %s == emoji, user, post, topic, forum
29 | 'UPDATE_POST_SELF' => '%s %s edited their %s in the topic %s located in the forum %s', // %s == emoji, user, post, topic, forum
30 | 'UPDATE_POST_OTHER' => '%s %s edited the %s written by %s in the topic %s located in the forum %s', // %s == emoji, edit user, post, user, topic, forum
31 | 'DELETE_POST' => '%s Deleted post by user %s in the topic %s located in the forum %s', // %s == emoji, user, topic, forum
32 | 'LOCK_POST' => '%s The %s written by user %s in the topic titled %s in the %s forum has been locked', // %s == emoji, post, user, topic, forum
33 | 'UNLOCK_POST' => '%s The %s written by user %s in the topic titled %s in the %s forum has been unlocked', // %s == emoji, post, user, topic, forum
34 |
35 | // Topic Notifications
36 | 'CREATE_TOPIC' => '%s %s created a new topic titled %s in the %s forum', // %s == emoji, user, topic, forum
37 | 'UPDATE_TOPIC_SELF' => '%s %s edited their topic %s located in the forum %s', // %s == emoji, user, topic, forum
38 | 'UPDATE_TOPIC_OTHER' => '%s %s edited the the topic %s written by %s located in the forum %s', // %s == emoji, edit user, topic, user, forum
39 | 'DELETE_TOPIC' => '%s Deleted the topic started by user %s titled \'%s\' containing %d posts in the forum %s', // %s/d == emoji, user, topic title, post count, forum
40 | 'LOCK_TOPIC' => '%s The topic titled %s in the %s forum started by user %s has been locked', // %s == emoji, topic, forum, user
41 | 'UNLOCK_TOPIC' => '%s The topic titled %s in the %s forum started by user %s has been unlocked', // %s == emoji, topic, forum, user
42 |
43 | // User Notifications
44 | 'CREATE_USER' => '%s New user account created for %s', // %s == emoji, user
45 | 'DELETE_USER' => '%s Deleted account for user %s', // %s == emoji, user
46 | 'DELETE_MULTI_USER' => '%s Deleted accounts for users %s', // %s == emoji, list of users
47 |
48 | // Additional Text
49 | 'PREVIEW' => 'Preview: ',
50 | 'REASON' => 'Reason: ',
51 | 'POST' => 'post',
52 | 'AND' => 'and',
53 | 'CONJ' => ',', // short for "conjunction character"
54 | 'OTHER' => 'other',
55 | 'OTHERS' => 'others',
56 | 'UNKNOWN_USER' => '{user}',
57 | 'UNKNOWN_FORUM' => '{forum}',
58 | 'UNKNOWN_TOPIC' => '{topic}',
59 | ));
60 |
--------------------------------------------------------------------------------
/language/en/info_acp_discord_notifications.php:
--------------------------------------------------------------------------------
1 | 'Discord Notifications',
22 | 'ACP_DISCORD_NOTIFICATIONS_TITLE' => 'Discord Notification Settings',
23 |
24 | // ACP Logs
25 | 'ACP_DISCORD_NOTIFICATIONS_LOG_UPDATE' => 'Modified Discord notification settings',
26 | ));
27 |
--------------------------------------------------------------------------------
/language/fr/acp_discord_notifications.php:
--------------------------------------------------------------------------------
1 |
8 | * @license GNU General Public License, version 2 (GPL-2.0-only)
9 | *
10 | * This file contains the language strings for the ACP settings page for this extension.
11 | */
12 |
13 | /**
14 | * DO NOT CHANGE
15 | */
16 | if (!defined('IN_PHPBB'))
17 | {
18 | exit;
19 | }
20 |
21 | if (empty($lang) || !is_array($lang))
22 | {
23 | $lang = array();
24 | }
25 |
26 | // DEVELOPERS PLEASE NOTE
27 | //
28 | // All language files should use UTF-8 as their encoding and the files must not contain a BOM.
29 | //
30 | // Placeholders can now contain order information, e.g. instead of
31 | // 'Page %s of %s' you can (and should) write 'Page %1$s of %2$s', this allows
32 | // translators to re-order the output of data while ensuring it remains correct
33 | //
34 | // You do not need this where single placeholders are used, e.g. 'Message %d' is fine
35 | // equally where a string contains only two placeholders which are used to wrap text
36 | // in a url you again do not need to specify an order e.g., 'Click %sHERE%s' is fine
37 | //
38 | // Some characters you may want to copy&paste:
39 | // ’ « » “ ” …
40 | //
41 |
42 | $lang = array_merge($lang, array(
43 | // ACP Extension Settings Page
44 | 'DN_ACP_DESCRIPTION' => 'Depuis cette page il est possible de définir les paramètres permettant à différents évènements du forum d’être envoyés comme notification sur un serveur Discord.',
45 |
46 | 'DN_MAIN_SETTINGS' => 'Paramètres généraux',
47 | 'DN_MASTER_ENABLE' => 'Activer les notifications Discord',
48 | 'DN_WEBHOOK_URL' => 'URL Webhook de Discord',
49 | 'DN_WEBHOOK_DESCRIPTION' => 'Permet de saisir l’adresse URL Webhook du serveur Discord généré par le serveur Discord. Voir cet article pour apprendre comment créer un nouveau Webhook.',
50 | 'DN_POST_PREVIEW_LENGTH' => 'Longueur de l’aperçu des messages',
51 | 'DN_POST_PREVIEW_DESCRIPTION' => 'Permet de saisir le nombre de caractères à afficher dans l’aperçu des messages (10 - 2000). Définir sur la valeur 0 pour désactiver l’aperçu des messages.',
52 | 'DN_TEST_MESSAGE' => 'Message de test',
53 | 'DN_TEST_MESSAGE_TEXT' => 'Texte du message de test : Hello Discord!',
54 | 'DN_TEST_DESCRIPTION' => 'Permet d’envoyer un message sur le serveur Discord afin de vérifier que la connexion avec phpBB est fonctionnelle.',
55 | 'DN_SEND_TEST' => 'Envoyer un message de test',
56 | 'DN_SEND_TEST_DESCRIPTION' => 'Permet d’envoyer le contenu du message de test au Webhook du serveur Discord. À utiliser pour vérifier que le Webhook est fonctionnel.',
57 |
58 | 'DN_TYPE_SETTINGS' => 'Types de notification',
59 | 'DN_TYPE_DESCRIPTION' => 'Permet de sélectionner quels types de notification devraient être générés sous forme de messages envoyés sur le serveur Discord.',
60 | 'DN_POST_CREATE' => 'Message créé',
61 | 'DN_POST_UPDATE' => 'Message modifié',
62 | 'DN_POST_DELETE' => 'Message supprimé',
63 | 'DN_POST_LOCK' => 'Message verrouillé',
64 | 'DN_POST_UNLOCK' => 'Message déverrouillé',
65 | 'DN_TOPIC_CREATE' => 'Sujet créé',
66 | 'DN_TOPIC_UPDATE' => 'Sujet modifié',
67 | 'DN_TOPIC_DELETE' => 'Sujet supprimé',
68 | 'DN_TOPIC_LOCK' => 'Sujet verrouillé',
69 | 'DN_TOPIC_UNLOCK' => 'Sujet déverrouillé',
70 | 'DN_USER_CREATE' => 'Compte utilisateur créé',
71 | 'DN_USER_DELETE' => 'Compte utilisateur supprimé',
72 |
73 | 'DN_FORUM_SETTINGS' => 'Forums concernés par les notifications',
74 | 'DN_FORUM_DESCRIPTION' => 'Permet de sélectionner les forums pour lesquels des évènements seront notifiés sur le serveur Discord, tels que ceux liés aux messages et aux sujets.',
75 |
76 | // Messages that appear after a user tries to send a test message
77 | 'DN_TEST_SUCCESS' => 'Test effectué avec succès ! Merci de vérifier sur le serveur Discord que le message de test apparait.',
78 | 'DN_TEST_FAILURE' => 'Un problème a été rencontré lors de l’envoi du message de test. Merci de vérifier l’adresse URL du Webhook du serveur Discord.',
79 | 'DN_TEST_BAD_MESSAGE' => 'Merci de saisir au moins un caractère pour le message de test.',
80 | 'DN_TEST_BAD_WEBHOOK' => 'Le texte saisi pour l’adresse URL Webhook du serveur Discord est incorrecte. Merci de vérifier ce paramètre puis d’essayer l’envoi à nouveau.',
81 |
82 | // Success/Failure messages that can be generated once the user saves
83 | 'DN_SETTINGS_SAVED' => 'Les paramètres de notification Discord ont été modifiés avec succès !',
84 | 'DN_MASTER_WEBHOOK_REQUIRED' => 'Une adresse URL Webhook du serveur Discord est requise pour activer les notifications.',
85 | 'DN_WEBHOOK_URL_INVALID' => 'L’adresse URL Webhook du serveur Discord doit être complète et correspondre à une adresse URL valide.',
86 | 'DN_POST_PREVIEW_INVALID' => 'La longueur de l’aperçu des messages doit être comprise entre 10 et 2000 ou définie sur 0 pour la désactivation de l’aperçu.',
87 | ));
88 |
--------------------------------------------------------------------------------
/language/fr/discord_notification_messages.php:
--------------------------------------------------------------------------------
1 |
8 | * @license GNU General Public License, version 2 (GPL-2.0-only)
9 | *
10 | * This file contains the language strings for the ACP settings page for this extension.
11 | */
12 |
13 | /**
14 | * DO NOT CHANGE
15 | */
16 | if (!defined('IN_PHPBB'))
17 | {
18 | exit;
19 | }
20 |
21 | if (empty($lang) || !is_array($lang))
22 | {
23 | $lang = array();
24 | }
25 |
26 | // DEVELOPERS PLEASE NOTE
27 | //
28 | // All language files should use UTF-8 as their encoding and the files must not contain a BOM.
29 | //
30 | // Placeholders can now contain order information, e.g. instead of
31 | // 'Page %s of %s' you can (and should) write 'Page %1$s of %2$s', this allows
32 | // translators to re-order the output of data while ensuring it remains correct
33 | //
34 | // You do not need this where single placeholders are used, e.g. 'Message %d' is fine
35 | // equally where a string contains only two placeholders which are used to wrap text
36 | // in a url you again do not need to specify an order e.g., 'Click %sHERE%s' is fine
37 | //
38 | // Some characters you may want to copy&paste:
39 | // ’ « » “ ” …
40 | //
41 |
42 | // These messages are used by the event/notification_event_listener class. The notifications naturally generate dynamic content,
43 | // and this is done using formatted strings passed to sprintf(). Each notification message below has a comment indicating what each
44 | // %s string argument should represent (typically this is a hyperlink with text describing a user, topic, post, or forum).
45 | // Note that the order of what gets populated in the %s arguments is unfortunately fixed, meaning that this could make good
46 | // translations into other difficult.
47 | $lang = array_merge($lang, array(
48 | // Post Notifications
49 | 'CREATE_POST' => '%s %s a publié un %s dans le sujet « %s » du forum « %s ».', // %s == emoji, user, post, topic, forum
50 | 'UPDATE_POST_SELF' => '%s %s a modifié son « %s dans le sujet « %s » du forum « %s ».', // %s == emoji, user, post, topic, forum
51 | 'UPDATE_POST_OTHER' => '%s %s a modifié le message « %s » publié par « %s » dans le sujet « %s » du forum « %s ».', // %s == emoji, edit user, post, user, topic, forum
52 | 'DELETE_POST' => '%s Message supprimé de l’auteur « %s » dans le sujet « %s » du forum « %s ».', // %s == emoji, user, topic, forum
53 | 'LOCK_POST' => '%s Le message « %s » publié par le membre « %s » dans le sujet intitulé « %s » du forum « %s » a été verrouillé.', // %s == emoji, post, user, topic, forum
54 | 'UNLOCK_POST' => '%s Le message « %s » publié par le membre « %s » dans le sujet intitulé « %s » du forum « %s » a été déverrouillé.', // %s == emoji, post, user, topic, forum
55 |
56 | // Topic Notifications
57 | 'CREATE_TOPIC' => '%s %s a publié un nouveau sujet intitulé « %s » dans le forum « %s ».', // %s == emoji, user, topic, forum
58 | 'UPDATE_TOPIC_SELF' => '%s %s a modifié son sujet « %s » dans le forum « %s ».', // %s == emoji, user, topic, forum
59 | 'UPDATE_TOPIC_OTHER' => '%s %s a modifié le sujet « %s », dont l’auteur est « %s » dans le forum « %s ».', // %s == emoji, edit user, topic, user, forum
60 | 'DELETE_TOPIC' => '%s Sujet supprimé de l’auteur « %s », intitulé « %s », contenant %d messages dans le forum « %s ».', // %s/d == emoji, user, topic title, post count, forum
61 | 'LOCK_TOPIC' => '%s Le sujet intitulé « %s » dans le forum « %s » et dont l’auteur est « %s » a été verrouillé.', // %s == emoji, topic, forum, user
62 | 'UNLOCK_TOPIC' => '%s Le sujet intitulé « %s » dans le forum « %s » et dont l’auteur est « %s » a été déverrouillé.', // %s == emoji, topic, forum, user
63 |
64 | // User Notifications
65 | 'CREATE_USER' => '%s Nouveau compte utilisateur créé pour le membre « %s ».', // %s == emoji, user
66 | 'DELETE_USER' => '%s Compte utilisateur supprimé pour le membre « %s ».', // %s == emoji, user
67 | 'DELETE_MULTI_USER' => '%s Comptes utilisateurs supprimés pour les membres : « %s ».', // %s == emoji, list of users
68 |
69 | // Additional Text
70 | 'PREVIEW' => 'Aperçu : ',
71 | 'REASON' => 'Raison : ',
72 | 'POST' => 'message',
73 | 'AND' => 'et',
74 | 'CONJ' => ',', // short for "conjunction character"
75 | 'OTHER' => 'autre',
76 | 'OTHERS' => 'autres',
77 | 'UNKNOWN_USER' => '{user}',
78 | 'UNKNOWN_FORUM' => '{forum}',
79 | 'UNKNOWN_TOPIC' => '{topic}',
80 | ));
81 |
--------------------------------------------------------------------------------
/language/fr/info_acp_discord_notifications.php:
--------------------------------------------------------------------------------
1 |
8 | * @license GNU General Public License, version 2 (GPL-2.0-only)
9 | *
10 | * This file contains the language strings for the ACP settings page for this extension.
11 | */
12 |
13 | /**
14 | * DO NOT CHANGE
15 | */
16 | if (!defined('IN_PHPBB'))
17 | {
18 | exit;
19 | }
20 |
21 | if (empty($lang) || !is_array($lang))
22 | {
23 | $lang = array();
24 | }
25 |
26 | // DEVELOPERS PLEASE NOTE
27 | //
28 | // All language files should use UTF-8 as their encoding and the files must not contain a BOM.
29 | //
30 | // Placeholders can now contain order information, e.g. instead of
31 | // 'Page %s of %s' you can (and should) write 'Page %1$s of %2$s', this allows
32 | // translators to re-order the output of data while ensuring it remains correct
33 | //
34 | // You do not need this where single placeholders are used, e.g. 'Message %d' is fine
35 | // equally where a string contains only two placeholders which are used to wrap text
36 | // in a url you again do not need to specify an order e.g., 'Click %sHERE%s' is fine
37 | //
38 | // Some characters you may want to copy&paste:
39 | // ’ « » “ ” …
40 | //
41 |
42 | $lang = array_merge($lang, array(
43 | // ACP Module
44 | 'ACP_DISCORD_NOTIFICATIONS' => 'Notifications Discord',
45 | 'ACP_DISCORD_NOTIFICATIONS_TITLE' => 'Paramètres',
46 |
47 | // ACP Logs
48 | 'ACP_DISCORD_NOTIFICATIONS_LOG_UPDATE' => 'Paramètres des notifications Discord modifiées',
49 | ));
50 |
--------------------------------------------------------------------------------
/license.txt:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.
5 | 675 Mass Ave, Cambridge, MA 02139, USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Library General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
--------------------------------------------------------------------------------
/migrations/extension_installation.php:
--------------------------------------------------------------------------------
1 | config['discord_notifications_enabled']);
29 | }
30 |
31 | /**
32 | * Add the discord notification enabled column to the forums table.
33 | * This setting determines whether or not activity on a specific forum will generate a
34 | * notification transmitted to Discord.
35 | *
36 | * @return array Array of table schema
37 | * @access public
38 | */
39 | public function update_schema()
40 | {
41 | return array(
42 | 'add_columns' => array(
43 | $this->table_prefix . 'forums' => array(
44 | 'discord_notifications_enabled' => array('BOOL', 0),
45 | ),
46 | ),
47 | );
48 | }
49 |
50 | /**
51 | * Drop the discord notification enabled column from the users table.
52 | *
53 | * @return array Array of table schema
54 | * @access public
55 | */
56 | public function revert_schema()
57 | {
58 | return array(
59 | 'drop_columns' => array(
60 | $this->table_prefix . 'forums' => array(
61 | 'discord_notifications_enabled',
62 | ),
63 | ),
64 | );
65 | }
66 |
67 | /**
68 | * Add Discord notification data to the database.
69 | * Note: these changes will automatically get reverted by phpbb if the extension is uninstalled.
70 | * Hence the reason why no corresponding revert_data() function exists.
71 | *
72 | * @return array Array of table data to set
73 | * @access public
74 | */
75 | public function update_data()
76 | {
77 | return array(
78 | // The "master switch" that enables notifications to be sent. This can only be set to true if the webhook URL is also valid
79 | array('config.add', array('discord_notifications_enabled', 0)),
80 | // The webhook generated by Discord to send the notifications to. Must be a valid URL.
81 | array('config.add', array('discord_notifications_webhook_url', '')),
82 | // The maximum number of characters permitted in a discord notification message
83 | array('config.add', array('discord_notifications_post_preview_length', 200)),
84 |
85 | // These configurations represent the various types of notifications that can be sent, which can be individually enabled or disabled.
86 | // Upon installation, every notification type is enabled by default.
87 |
88 | // Post notifications
89 | array('config.add', array('discord_notification_type_post_create', 1)),
90 | array('config.add', array('discord_notification_type_post_update', 1)),
91 | array('config.add', array('discord_notification_type_post_delete', 1)),
92 | array('config.add', array('discord_notification_type_post_lock', 1)),
93 | array('config.add', array('discord_notification_type_post_unlock', 1)),
94 |
95 | // Topic notifications
96 | array('config.add', array('discord_notification_type_topic_create', 1)),
97 | array('config.add', array('discord_notification_type_topic_update', 1)),
98 | array('config.add', array('discord_notification_type_topic_delete', 1)),
99 | array('config.add', array('discord_notification_type_topic_lock', 1)),
100 | array('config.add', array('discord_notification_type_topic_unlock', 1)),
101 |
102 | // User notifications
103 | array('config.add', array('discord_notification_type_user_create', 1)),
104 | array('config.add', array('discord_notification_type_user_delete', 1)),
105 |
106 | // Standard ACP module data
107 | array('module.add', array(
108 | 'acp',
109 | 'ACP_CAT_DOT_MODS',
110 | 'ACP_DISCORD_NOTIFICATIONS'
111 | )),
112 | array('module.add', array(
113 | 'acp',
114 | 'ACP_DISCORD_NOTIFICATIONS',
115 | array(
116 | 'module_basename' => '\roots\discordnotifications\acp\discord_notifications_module',
117 | 'modes' => array('settings'),
118 | ),
119 | )),
120 | );
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/notification_service.php:
--------------------------------------------------------------------------------
1 | config = $config;
43 | $this->db = $db;
44 | }
45 |
46 | /**
47 | * Check whether notifications are enabled for a certain type
48 | * @param $notification_type The name of the notification type to check
49 | * @return False if the global notification setting is disabled or this notification type is disabled
50 | */
51 | public function is_notification_type_enabled($notification_type)
52 | {
53 | // Also check the global extension enabled setting. We don't generate any notifications if this is disabled
54 | if ($this->config['discord_notifications_enabled'] == 1 && $this->config[$notification_type] == 1)
55 | {
56 | return true;
57 | }
58 |
59 | return false;
60 | }
61 |
62 | /**
63 | * Check whether notifications that occur on a specific forum should be generated
64 | * @param $forum_id The ID of the forum to check
65 | * @return False if the global notification setting is disabled, notifications are disabled for the forum, or no forum exists with this ID
66 | */
67 | public function is_notification_forum_enabled($forum_id)
68 | {
69 | if (is_numeric($forum_id) == false)
70 | {
71 | return false;
72 | }
73 |
74 | if ($this->config['discord_notifications_enabled'] == 0)
75 | {
76 | return false;
77 | }
78 |
79 | // Query the forum table where forum notification settings are stored
80 | $sql = "SELECT discord_notifications_enabled FROM " . FORUMS_TABLE . " WHERE forum_id = $forum_id";
81 | $result = $this->db->sql_query($sql);
82 | $data = $this->db->sql_fetchrow($result);
83 | $enabled = $data['discord_notifications_enabled'] == 1 ? true : false;
84 | $this->db->sql_freeresult($result);
85 |
86 | return $enabled;
87 | }
88 |
89 | /**
90 | * Retrieve the value for the ACP settings configuration related to post preview length
91 | * @return The number of characters to display in the post preview. A zero value indicates that no preview should be displayed
92 | */
93 | public function get_post_preview_length()
94 | {
95 | return $this->config['discord_notifications_post_preview_length'];
96 | }
97 |
98 | /**
99 | * Retrieves the name of a forum from the database when given an ID
100 | * @param $forum_id The ID of the forum to query
101 | * @return The name of the forum, or NULL if not found
102 | */
103 | public function query_forum_name($forum_id)
104 | {
105 | if (is_numeric($forum_id) == false)
106 | {
107 | return null;
108 | }
109 |
110 | $sql = "SELECT forum_name from " . FORUMS_TABLE . " WHERE forum_id = $forum_id";
111 | $result = $this->db->sql_query($sql);
112 | $data = $this->db->sql_fetchrow($result);
113 | $name = $data['forum_name'];
114 | $this->db->sql_freeresult($result);
115 | return $name;
116 | }
117 |
118 | /**
119 | * Retrieves the subject of a post from the database when given an ID
120 | * @param $post_id The ID of the post to query
121 | * @return The subject of the post, or NULL if not found
122 | */
123 | public function query_post_subject($post_id)
124 | {
125 | if (is_numeric($post_id) == false)
126 | {
127 | return null;
128 | }
129 |
130 | $sql = "SELECT post_subject from " . POSTS_TABLE . " WHERE post_id = $post_id";
131 | $result = $this->db->sql_query($sql);
132 | $data = $this->db->sql_fetchrow($result);
133 | $subject = $data['post_subject'];
134 | $this->db->sql_freeresult($result);
135 | return $subject;
136 | }
137 |
138 | /**
139 | * Retrieves the title of a topic from the database when given an ID
140 | * @param $topic_id The ID of the topic to query
141 | * @return The name of the topic, or NULL if not found
142 | */
143 | public function query_topic_title($topic_id)
144 | {
145 | if (is_numeric($topic_id) == false)
146 | {
147 | return null;
148 | }
149 |
150 | $sql = "SELECT topic_title from " . TOPICS_TABLE . " WHERE topic_id = $topic_id";
151 | $result = $this->db->sql_query($sql);
152 | $data = $this->db->sql_fetchrow($result);
153 | $title = $data['topic_title'];
154 | $this->db->sql_freeresult($result);
155 | return $title;
156 | }
157 |
158 | /**
159 | * Runs a query to fetch useful data about a specific forum topic. The return data includes information on the first poster, number of posts,
160 | * which forum contains the topic, and more.
161 | * @param $topic_id The ID of the topic to query
162 | * @return Array containing data about the topic and the forum it is contained in
163 | */
164 | public function query_topic_details($topic_id)
165 | {
166 | if (is_numeric($topic_id) == false)
167 | {
168 | return array();
169 | }
170 |
171 | $topic_table = TOPICS_TABLE;
172 | $forum_table = FORUMS_TABLE;
173 | $sql = "SELECT
174 | f.forum_id, f.forum_name,
175 | t.topic_id, t.topic_title, t.topic_poster, t.topic_first_post_id, t.topic_first_poster_name, t.topic_posts_approved, t.topic_visibility
176 | FROM
177 | $forum_table f, $topic_table t
178 | WHERE
179 | t.forum_id = f.forum_id and t.topic_id = $topic_id";
180 | $result = $this->db->sql_query($sql);
181 | $data = $this->db->sql_fetchrow($result);
182 | $this->db->sql_freeresult($result);
183 |
184 | return $data;
185 | }
186 |
187 | /**
188 | * Retrieves the name of a user from the database when given an ID
189 | * @param $user_id The ID of the user to query
190 | * @return The name of the user, or NULL if not found
191 | */
192 | public function query_user_name($user_id)
193 | {
194 | if (is_numeric($user_id) == false)
195 | {
196 | return null;
197 | }
198 |
199 | $sql = "SELECT username from " . USERS_TABLE . " WHERE user_id = $user_id";
200 | $result = $this->db->sql_query($sql);
201 | $data = $this->db->sql_fetchrow($result);
202 | $name = $data['username'];
203 | $this->db->sql_freeresult($result);
204 | return $name;
205 | }
206 |
207 | /**
208 | * Sends a notification message to Discord. This function checks the master switch configuration for the extension, but does
209 | * no further checks. The caller is responsible for performing full validation of the notification prior to calling this function.
210 | * @param $color The color to use in the notification (decimal value of a hexadecimal RGB code)
211 | * @param $message The message text to send.
212 | * @param $footer Text to place in the footer of the message. Optional.
213 | */
214 | public function send_discord_notification($color, $message, $footer = NULL)
215 | {
216 | if ($this->config['discord_notifications_enabled'] == 0 || isset($message) == false)
217 | {
218 | return;
219 | }
220 |
221 | // Note that the value stored in the config table will always be a valid URL when discord_notifications_enabled is set
222 | $discord_webhook_url = $this->config['discord_notifications_webhook_url'];
223 |
224 | $this->execute_discord_webhook($discord_webhook_url, $color, $message, $footer);
225 | }
226 |
227 | /**
228 | * Sends a message to Discord, disregarding any configurations that are currently set. This method is primarily used by users
229 | * to test their notifications from the ACP.
230 | * @param $discord_webhook_url The URL of the Discord webhook to transmit the message to. If this is an invalid URL, no message will be sent.
231 | * @param $message The message text to send. Must be a non-empty string.
232 | * @return Boolean indicating whether the message transmission resulted in success or failure.
233 | */
234 | public function force_send_discord_notification($discord_webhook_url, $message)
235 | {
236 | if (!filter_var($discord_webhook_url, FILTER_VALIDATE_URL) || is_string($message) == false || $message == '')
237 | {
238 | return false;
239 | }
240 |
241 | return $this->execute_discord_webhook($discord_webhook_url, self::DEFAULT_COLOR, $message, NULL);
242 | }
243 |
244 | /**
245 | * Helper function that performs the message transmission. This method checks the inputs to prevent any problematic characters in
246 | * strings. Note that this function checks that the message and footer do not exceed the maximum allowable limits by the Discord
247 | * API, but it does -not- check configuration settings such as the post_preview_length. The code invoking this method is responsible
248 | * for checking those settings.
249 | *
250 | * @param $discord_webhook_url The URL of the Discord webhook to transmit the message to.
251 | * @param $color Color to set for the message. Should be a positive non-zero integer representing a hex color code.
252 | * @param $message The message text to send. Must be a non-empty string.
253 | * @param $footer The text to place in the footer. Optional. Must be a non-empty string.
254 | * @return Boolean indicating whether the message transmission resulted in success or failure.
255 | * @see https://discordapp.com/developers/docs/resources/webhook#execute-webhook
256 | */
257 | private function execute_discord_webhook($discord_webhook_url, $color, $message, $footer = NULL)
258 | {
259 | if (isset($discord_webhook_url) == false || $discord_webhook_url === '')
260 | {
261 | return false;
262 | }
263 | if (is_integer($color) == false || $color < 0)
264 | {
265 | // Use the default color if we did not receive a valid color value
266 | $color = self::DEFAULT_COLOR;
267 | }
268 | if (is_string($message) == false || $message == '')
269 | {
270 | return false;
271 | }
272 | if (isset($footer) == true && (is_string($footer) == false || $footer == ''))
273 | {
274 | return false;
275 | }
276 |
277 | // Clean up the message and footer text before sending by trimming whitespace from the front and end of the message and footer strings.
278 | $message = trim($message);
279 | $message = str_replace('"', "'", $message); // Replace " characters that would break the JSON encoding that our message must be wrapped in.
280 | if (isset($footer))
281 | {
282 | $footer = trim($footer);
283 | $footer = str_replace('"', "'", $footer);
284 | // Discord does not appear to allow newline characters in the footer. In fact, the presence of them causes the message POST
285 | // to fail. Hence why we replace all newlines with a space here.
286 | $footer = str_replace(array("\r", "\n"), ' ', $footer);
287 | }
288 |
289 | // Abort if we find that either of our text fields are now empty strings
290 | if ($message === '')
291 | {
292 | return false;
293 | }
294 | if (isset($footer) && $footer === '')
295 | {
296 | return false;
297 | }
298 |
299 | // Verify that the message and footer size is within the allowable limit and truncate if necessary. We add "..." as the last three characters
300 | // when we require truncation.
301 | if (strlen($message) > self::MAX_MESSAGE_SIZE)
302 | {
303 | $message = substr($message, 0, self::MAX_MESSAGE_SIZE - 3) . '...';
304 | }
305 | if (isset($footer))
306 | {
307 | if (strlen($footer) > self::MAX_FOOTER_SIZE)
308 | {
309 | $footer = substr($footer, 0, self::MAX_FOOTER_SIZE - 3) . '...';
310 | }
311 | }
312 |
313 | // Place the message inside the JSON structure that Discord expects to receive at the REST endpoint.
314 | $post = '';
315 | if (isset($footer))
316 | {
317 | $post = sprintf('{"embeds": [{"color": "%d", "description" : "%s", "footer": {"text": "%s"}}]}', $color, $message, $footer);
318 | }
319 | else {
320 | $post = sprintf('{"embeds": [{"color": "%d", "description" : "%s"}]}', $color, $message);
321 | }
322 |
323 | // Use the CURL library to transmit the message via a POST operation to the webhook URL.
324 | $h = curl_init();
325 | curl_setopt($h, CURLOPT_URL, $discord_webhook_url);
326 | curl_setopt($h, CURLOPT_POST, 1);
327 | curl_setopt($h, CURLOPT_POSTFIELDS, $post);
328 | $response = curl_exec($h);
329 | curl_close($h);
330 |
331 | // Check if the response was not successful
332 | if (is_array($response) && $response['message'])
333 | {
334 | // TODO: If the response includes a message then an error has occurred. Determine whether we want to log it, queue it up to try again, etc.
335 | return false;
336 | }
337 |
338 | return true;
339 | }
340 | }
341 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
15 |
16 |
17 | ./tests
18 | ./tests/functional
19 |
20 |
21 | ./tests/functional/
22 |
23 |
24 |
25 |
26 |
27 | ./tests/
28 |
29 |
30 | ./
31 |
32 | ./language/
33 | ./migrations/
34 | ./tests/
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/tests/dbal/fixtures/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | config_name
5 | config_value
6 | is_dynamic
7 |
8 | config1
9 | foo
10 | 0
11 |
12 |
13 | config2
14 | bar
15 | 1
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/tests/dbal/simple_test.php:
--------------------------------------------------------------------------------
1 | createXMLDataSet(__DIR__ . '/fixtures/config.xml');
29 | }
30 |
31 | public function test_column()
32 | {
33 | $this->db = $this->new_dbal();
34 |
35 | if (phpbb_version_compare(PHPBB_VERSION, '3.2.0-dev', '<'))
36 | {
37 | // This is how to instantiate db_tools in phpBB 3.1
38 | $db_tools = new \phpbb\db\tools($this->db);
39 | }
40 | else
41 | {
42 | // This is how to instantiate db_tools in phpBB 3.2
43 | $factory = new \phpbb\db\tools\factory();
44 | $db_tools = $factory->get($this->db);
45 | }
46 |
47 | $this->assertTrue($db_tools->sql_column_exists(FORUMS_TABLE, 'discord_notifications_enabled'), 'Asserting that column "discord_notifications_enabled" exists on forums table');
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/tests/functional/demo_test.php:
--------------------------------------------------------------------------------
1 |
6 | # @license GNU General Public License, version 2 (GPL-2.0)
7 | #
8 | # For full copyright and license information, please see
9 | # the docs/CREDITS.txt file.
10 | #
11 | set -e
12 | set -x
13 |
14 | EXTNAME=$1
15 | BRANCH=$2
16 | EXTPATH_TEMP=$3
17 |
18 | # Copy extension to a temp folder
19 | mkdir ../../tmp
20 | cp -R . ../../tmp
21 | cd ../../
22 |
23 | # Clone phpBB
24 | git clone --depth=1 "git://github.com/phpbb/phpbb.git" "phpBB3" --branch=$BRANCH
25 |
--------------------------------------------------------------------------------