Changeset 11783
- Timestamp:
- 04/22/2022 07:55:13 PM (2 years ago)
- Location:
- sites/trunk/common/includes/slack/props
- Files:
-
- 1 added
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
sites/trunk/common/includes/slack/props/lib.php
r11761 r11783 3 3 namespace Dotorg\Slack\Props; 4 4 use Dotorg\Slack\Send; 5 use function Dotorg\Profiles\{ post as profiles_post }; 5 6 6 7 function show_error( $user ) { … … 10 11 /** 11 12 * Receive `/props` request and send to `#props`. 13 * 14 * This is being deprecated in favor of `handle_props_message()`. 12 15 * 13 16 * @param array $data … … 57 60 return sprintf( "Your props to @%s have been sent.\n", $receiver ); 58 61 } 62 63 /** 64 * Adds props in Slack to w.org profiles. 65 * 66 * Receives webhook notifications for all new messages in `#props`, 67 */ 68 function handle_props_message( object $request ) : string { 69 if ( ! is_valid_props( $request->event ) ) { 70 return 'Invalid props'; 71 } 72 73 $giver_user = map_slack_users_to_wporg( array( $request->event->user ) )[0]; 74 $recipient_slack_ids = get_recipient_slack_ids( $request->event->blocks ); 75 76 if ( empty( $recipient_slack_ids ) ) { 77 return 'Nobody was mentioned'; 78 } 79 80 $recipient_users = map_slack_users_to_wporg( $recipient_slack_ids ); 81 $recipient_ids = array_column( $recipient_users, 'id' ); 82 83 $url = sprintf( 84 'https://wordpress.slack.com/archives/%s/p%s', 85 $request->event->channel, 86 $request->event->ts 87 ); 88 $url = filter_var( $url, FILTER_SANITIZE_URL ); 89 90 if ( empty( $recipient_ids ) ) { 91 return 'No recipients'; 92 } 93 94 // This is Slack's unintuitive way of giving messages a unique ID :| 95 // https://api.slack.com/messaging/retrieving#individual_messages 96 $message_id = sprintf( '%s-%s', $request->event->channel, $request->event->ts ); 97 $message = prepare_message( $request->event->text, $recipient_users ); 98 99 add_activity_to_profile( compact( 'giver_user', 'recipient_ids', 'url', 'message_id', 'message' ) ); 100 101 // The request was successful from Slack's perspective as long as we received and validated it. Any errors 102 // that occurred when pushing to Profiles are only significant to us. 103 return 'Success'; 104 } 105 106 /** 107 * Determine if this is an event that we should handle. 108 */ 109 function is_valid_props( object $event ) : bool { 110 $valid_channels = array( 111 'C0FRG66LR' #props 112 ); 113 114 if ( defined( 'WPORG_SANDBOXED' ) && WPORG_SANDBOXED ) { 115 $valid_channels[] = 'C03AKLN7P9U'; #iandunn-testing 116 } 117 118 $has_required_params = isset( $event->channel, $event->blocks, $event->type ) && is_array( $event->blocks ); 119 $in_valid_channel = in_array( $event->channel, $valid_channels, true ); 120 121 $is_correct_type = 122 'message' === $event->type && 123 empty( $event->subtype ) && // e.g., `message.deleted`, `message.changed`. 124 empty( $event->hidden ) && 125 empty( $event->thread_ts ); 126 127 if ( $is_correct_type && $has_required_params && $in_valid_channel ) { 128 return true; 129 } 130 131 return false; 132 } 133 134 /** 135 * Parse the mentioned Slack user IDs from a message event. 136 * 137 * This assumes that the app is configured to escape usernames. 138 */ 139 function get_recipient_slack_ids( array $blocks ) : array { 140 $ids = array(); 141 142 foreach ( $blocks as $block ) { 143 foreach ( $block->elements as $element ) { 144 foreach ( $element->elements as $inner_element ) { 145 if ( 'user' !== $inner_element->type ) { 146 continue; 147 } 148 149 $ids[] = $inner_element->user_id; 150 } 151 } 152 } 153 154 return $ids; 155 } 156 157 /** 158 * Find the w.org users associated with the given slack accounts. 159 */ 160 function map_slack_users_to_wporg( array $slack_ids ) : array { 161 global $wpdb; 162 163 if ( empty( $slack_ids ) ) { 164 return array(); 165 } 166 167 $wporg_users = array(); 168 $id_placeholders = implode( ', ', array_fill( 0, count( $slack_ids ), '%s' ) ); 169 170 $query = $wpdb->prepare( " 171 SELECT 172 su.slack_id, su.user_id AS wporg_id, 173 mu.user_login 174 FROM `slack_users` su 175 JOIN `minibb_users` mu ON su.user_id = mu.ID 176 WHERE `slack_id` IN( $id_placeholders )", 177 $slack_ids 178 ); 179 180 $results = $wpdb->get_results( $query, ARRAY_A ); 181 182 foreach ( $results as $user ) { 183 $wporg_users[ $user['slack_id'] ] = array( 184 'id' => (int) $user['wporg_id'], 185 'user_login' => $user['user_login'], 186 ); 187 } 188 189 return $wporg_users; 190 } 191 192 /** 193 * Replace Slack IDs with w.org usernames, to better fit w.org profiles. 194 */ 195 function prepare_message( string $original, array $user_map ) : string { 196 $search = array(); 197 $replace = array(); 198 199 foreach ( $user_map as $slack_id => $wporg_user ) { 200 $search[] = sprintf( '<@%s>', $slack_id ); 201 $replace[] = '@' . $wporg_user['user_login']; 202 } 203 204 return str_replace( $search, $replace, $original ); 205 } 206 207 /** 208 * Send a request to Profiles to add the activity. 209 * 210 * See `handle_slack_activity()` in `buddypress.org/.../wporg-profiles-activity-handler.php` for the needed args. 211 */ 212 function add_activity_to_profile( array $request_args ) : bool { 213 require_once dirname( __DIR__, 2 ) . '/profiles/profiles.php'; 214 215 $request_args = array_merge( 216 $request_args, 217 array( 218 'action' => 'wporg_handle_activity', 219 'source' => 'slack', 220 'activity' => "props_given", 221 ) 222 ); 223 224 $response_body = profiles_post( $request_args ); 225 226 if ( is_numeric( $response_body ) && (int) $response_body > 0 ) { 227 $success = true; 228 229 } else { 230 $success = false; 231 232 trigger_error( 'Adding activity failed with error: ' . $response_body, E_USER_WARNING ); 233 } 234 235 return $success; 236 } -
sites/trunk/common/includes/slack/props/tests/test-lib.php
r11763 r11783 2 2 3 3 namespace Dotorg\Slack\Props\Tests; 4 use wpdbStub; 4 5 use PHPUnit\Framework\TestCase; 5 use function Dotorg\Slack\Props\{ run };6 use function Dotorg\Slack\Props\{ run, is_valid_props, get_recipient_slack_ids, map_slack_users_to_wporg, prepare_message }; 6 7 7 8 /** … … 9 10 * @group props 10 11 */ 11 class Test_Props extends TestCase {12 class Test_Props_Lib extends TestCase { 12 13 public static function setUpBeforeClass() : void { 13 14 require_once dirname( __DIR__ ) . '/lib.php'; 15 } 16 17 protected static function get_valid_request() { 18 $json = file_get_contents( __DIR__ . '/valid-request.json' ); 19 20 return json_decode( $json ); 14 21 } 15 22 … … 90 97 return $cases; 91 98 } 99 100 /** 101 * @covers ::is_valid_props 102 * @dataProvider data_is_valid_props 103 * @group unit 104 */ 105 public function test_is_valid_props( object $event, bool $expected ) : void { 106 $actual = is_valid_props( $event ); 107 108 $this->assertSame( $expected, $actual ); 109 } 110 111 public function data_is_valid_props() : array { 112 $valid_request = self::get_valid_request(); 113 114 $wrong_channel_event = json_decode( json_encode( $valid_request->event ) ); 115 $wrong_channel_event->channel = 'C01234567'; 116 117 $reaction_event = json_decode( json_encode( $valid_request->event ) ); 118 $reaction_event->type = 'reaction_added'; 119 120 $deleted_event = json_decode( json_encode( $valid_request->event ) ); 121 $deleted_event->subtype = 'message_deleted'; 122 123 $hidden_event = json_decode( json_encode( $valid_request->event ) ); 124 $hidden_event->hidden = true; 125 126 $thread_event = json_decode( json_encode( $valid_request->event ) ); 127 $thread_event->thread_ts = $valid_request->event->ts; 128 129 $cases = array( 130 'missing critical properties' => array( 131 'request' => (object) array( 'foo' => 'bar' ), 132 'expected' => false, 133 ), 134 135 'wrong channel' => array( 136 'request' => $wrong_channel_event, 137 'expected' => false, 138 ), 139 140 'wrong type' => array( 141 'request' => $reaction_event, 142 'expected' => false, 143 ), 144 145 'wrong subtype' => array( 146 'request' => $deleted_event, 147 'expected' => false, 148 ), 149 150 'hidden' => array( 151 'request' => $hidden_event, 152 'expected' => false, 153 ), 154 155 'reply in thread' => array( 156 'request' => $thread_event, 157 'expected' => false, 158 ), 159 160 'valid event' => array( 161 'request' => $valid_request->event, 162 'expected' => true, 163 ), 164 ); 165 166 return $cases; 167 } 168 169 /** 170 * @covers ::get_recipient_slack_ids 171 * @dataProvider data_get_recipient_slack_ids 172 * @group unit 173 */ 174 public function test_get_recipient_slack_ids( array $blocks, array $expected ) : void { 175 $actual = get_recipient_slack_ids( $blocks ); 176 177 $this->assertSame( $expected, $actual ); 178 } 179 180 public function data_get_recipient_slack_ids() : array { 181 $valid_request = self::get_valid_request(); 182 183 $cases = array( 184 'empty' => array( 185 'blocks' => array(), 186 'expected' => array(), 187 ), 188 189 'valid' => array( 190 'blocks' => $valid_request->event->blocks, 191 'expected' => array( 192 'U02RR6SGY', 193 'U02RQHNND', 194 'U3KJ0TK4L', 195 'U4L99HZB6', 196 'U024MFP4L', 197 'U6R2E3Y9Y', 198 'U023GFZJ07L', 199 'U1E5RLU1L', 200 ), 201 ), 202 ); 203 204 return $cases; 205 } 206 207 /** 208 * @covers ::map_slack_users_to_wporg 209 * @dataProvider data_map_slack_users_to_wporg 210 * @group unit 211 */ 212 public function test_map_slack_users_to_wporg( array $slack_ids, array $db_results, array $expected ) : void { 213 global $wpdb; 214 215 $wpdb = $this->createStub( wpdbStub::class ); 216 $wpdb->method( 'get_results' )->willReturn( $db_results ); 217 218 $actual = map_slack_users_to_wporg( $slack_ids ); 219 220 $this->assertSame( $expected, $actual ); 221 } 222 223 public function data_map_slack_users_to_wporg() : array { 224 $cases = array( 225 'empty' => array( 226 'slack_ids' => array(), 227 'db_results' => array(), 228 'expected' => array(), 229 ), 230 231 'valid giver' => array( 232 'slack_ids' => array( 'U02QCF502' ), 233 234 'db_results' => array( 235 array( 236 'slack_id' => 'U02QCF502', 237 'wporg_id' => '33690', 238 'user_login' => 'iandunn', 239 ), 240 ), 241 242 'expected' => array( 243 'U02QCF502' => array( 244 'id' => 33690, 245 'user_login' => 'iandunn', 246 ), 247 ), 248 ), 249 250 'valid receivers' => array( 251 'slack_ids' => array( 'U02RQHNND', 'U02RR6SGY', 'U3KJ0TK4L', 'U4L99HZB6' ), 252 253 'db_results' => array( 254 array( 255 'slack_id' => 'U02RQHNND', 256 'wporg_id' => '297445', 257 'user_login' => 'SergeyBiryukov', 258 ), 259 260 array( 261 'slack_id' => 'U02RR6SGY', 262 'wporg_id' => '2255796', 263 'user_login' => 'Mamaduka', 264 ), 265 266 267 array( 268 'slack_id' => 'U3KJ0TK4L', 269 'wporg_id' => '15049054', 270 'user_login' => 'davidbaumwald', 271 ), 272 273 array( 274 'slack_id' => 'U4L99HZB6', 275 'wporg_id' => '8976791', 276 'user_login' => 'pbiron', 277 ), 278 ), 279 280 'expected' => array( 281 'U02RQHNND' => array( 282 'id' => 297445, 283 'user_login' => 'SergeyBiryukov', 284 ), 285 'U02RR6SGY' => array( 286 'id' => 2255796, 287 'user_login' => 'Mamaduka', 288 ), 289 'U3KJ0TK4L' => array( 290 'id' => 15049054, 291 'user_login' => 'davidbaumwald', 292 ), 293 294 'U4L99HZB6' => array( 295 'id' => 8976791, 296 'user_login' => 'pbiron', 297 ), 298 ), 299 ), 300 ); 301 302 return $cases; 303 } 304 305 /** 306 * @covers ::prepare_message 307 * @dataProvider data_prepare_message 308 * @group unit 309 */ 310 public function test_prepare_message( string $text, array $user_map, string $expected ) : void { 311 $actual = prepare_message( $text, $user_map ); 312 313 $this->assertSame( $expected, $actual ); 314 } 315 316 public function data_prepare_message() : array { 317 $valid_request = self::get_valid_request(); 318 319 $cases = array( 320 'empty' => array( 321 'text' => '', 322 'user_map' => array(), 323 'expected' => '', 324 ), 325 326 'valid' => array( 327 'text' => $valid_request->event->text, 328 'user_map' => array( 329 'U023GFZJ07L' => array( 330 'id' => 18752239, 331 'user_login' => 'costdev', 332 ), 333 'U024MFP4L' => array( 334 'id' => 2545, 335 'user_login' => 'markjaquith', 336 ), 337 338 'U02RQHNND' => array( 339 'id' => 297445, 340 'user_login' => 'SergeyBiryukov', 341 ), 342 343 'U02RR6SGY' => array( 344 'id' => 2255796, 345 'user_login' => 'Mamaduka', 346 ), 347 348 'U1E5RLU1L' => array( 349 'id' => 15152479, 350 'user_login' => 'jeroenrotty', 351 ), 352 353 'U3KJ0TK4L' => array( 354 'id' => 15049054, 355 'user_login' => 'davidbaumwald', 356 ), 357 358 'U4L99HZB6' => array( 359 'id' => 8976791, 360 'user_login' => 'pbiron', 361 ), 362 363 'U6R2E3Y9Y' => array( 364 'id' => 15524609, 365 'user_login' => 'webcommsat', 366 ), 367 ), 368 'expected' => 'props to @Mamaduka for co-leading 5.9.3 RC 1, to @SergeyBiryukov for running mission control and to @davidbaumwald @pbiron @markjaquith @webcommsat @costdev @jeroenrotty for their help testing the release package :community: :wordpress:', 369 ), 370 ); 371 372 return $cases; 373 } 92 374 }
Note: See TracChangeset
for help on using the changeset viewer.