Skip to content

Commit

Permalink
Migrate Slack screenshots to new API calls
Browse files Browse the repository at this point in the history
  • Loading branch information
ThexXTURBOXx committed May 7, 2024
1 parent 7388d24 commit 4ab026c
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 32 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## 1.3.0
* Migrate Slack screenshot API calls to `files.*UploadExternal` (you now need to specify also a `channelId` in the `SlackHandler` for that!)

## 1.2.6
* Allow `package_info_plus` versions `8.x`
* Remove direct dependency on `device_info_plus_platform_interface` (why was this there anyway?)
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -827,7 +827,9 @@ main() {

All parameters list:
* webhookUrl (required) - url of your webhook
* channel (required) - your channel name (i.e. #catcher2)
* channel (required) - your channel name (e.g. #catcher2)
* apiToken (optional) - your API token, only needed for screenshots (e.g. xxxx-xxxxxxxxx-xxxx)
* channelId (optional) - your screenshot channel ID, only needed for screenshots (e.g. C0NF841BK)
* username (optional) - name of the integration bot
* iconEmoji (optional) - avatar of the integration bot
* enableDeviceParameters (optional) - please look in console handler description
Expand Down
134 changes: 104 additions & 30 deletions lib/handlers/slack_handler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@ import 'package:catcher_2/model/platform_type.dart';
import 'package:catcher_2/model/report.dart';
import 'package:catcher_2/model/report_handler.dart';
import 'package:catcher_2/utils/catcher_2_utils.dart';
import 'package:cross_file/cross_file.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';

/// Slack webhook API doesn't allow file attachments
class SlackHandler extends ReportHandler {
SlackHandler(
this.webhookUrl,
this.channel, {
this.apiToken,
this.channelId,
this.username = 'Catcher 2',
this.iconEmoji = ':bangbang:',
this.printLogs = false,
Expand All @@ -28,6 +29,7 @@ class SlackHandler extends ReportHandler {
final String webhookUrl;
final String? apiToken;
final String channel;
final String? channelId;
final String username;
final String iconEmoji;

Expand Down Expand Up @@ -62,35 +64,9 @@ class SlackHandler extends ReportHandler {
};
_printLog('Sending request to Slack server...');

if (apiToken != null && screenshot != null) {
final screenshotPath = screenshot.path;
final formData = FormData.fromMap(<String, dynamic>{
'token': apiToken,
'channels': channel,
'file': await MultipartFile.fromFile(screenshotPath),
});
final responseFile = await _dio.post<dynamic>(
'https://slack.com/api/files.upload',
data: formData,
options: Options(
contentType: Headers.multipartFormDataContentType,
),
);
if (responseFile.data != null &&
responseFile.data['file'] != null &&
responseFile.data['file']['url_private'] != null) {
data.addAll({
'attachments': [
{
'image_url': responseFile.data['file']['url_private'],
'text': 'Error Screenshot',
},
],
});
}
_printLog(
'Server responded upload file with code: ${responseFile.statusCode} '
'and message upload file: ${responseFile.statusMessage}',
if (screenshot != null) {
data.addAll(
await _tryUploadScreenshot(screenshot: XFile(screenshot.path)),
);
}

Expand All @@ -108,6 +84,104 @@ class SlackHandler extends ReportHandler {
}
}

Future<Map<String, dynamic>> _tryUploadScreenshot({
required XFile screenshot,
}) async {
if (apiToken == null || channelId == null) {
_printLog(
'Cannot send screenshot to Slack because either '
'apiToken or channelId is not set!',
);
return {};
}

try {
final screenshotPath = screenshot.path;
final name = 'catcher_2_${DateTime.now().microsecondsSinceEpoch}.png';

final formData = FormData.fromMap(<String, dynamic>{
'token': apiToken,
'filename': name,
'length': await screenshot.length(),
'alt_txt': 'Error Screenshot',
});
final responseFile = await _dio.post<dynamic>(
'https://slack.com/api/files.getUploadURLExternal',
data: formData,
options: Options(
contentType: Headers.formUrlEncodedContentType,
),
);
if (responseFile.data == null ||
responseFile.data['ok'] != true ||
responseFile.data['upload_url'] == null ||
responseFile.data['file_id'] == null) {
_printLog(
'Server responded to getUploadURLExternal with code: '
'${responseFile.statusCode} '
'and message upload file: ${responseFile.statusMessage}',
);
return {};
}

final formDataPost = FormData.fromMap(<String, dynamic>{
'file': await MultipartFile.fromFile(screenshotPath),
});
final responseFilePost = await _dio.post<dynamic>(
responseFile.data['upload_url'],
data: formDataPost,
options: Options(
contentType: Headers.multipartFormDataContentType,
),
);
if (responseFilePost.statusCode != 200) {
_printLog(
'Server responded to upload file post with code: '
'${responseFilePost.statusCode} '
'and message upload file: ${responseFilePost.statusMessage}',
);
return {};
}

final formDataComplete = FormData.fromMap(<String, dynamic>{
'token': apiToken,
'files': '[{"id":"${responseFile.data['file_id']}"}]',
'channel_id': channelId,
});
final responseFileComplete = await _dio.post<dynamic>(
'https://slack.com/api/files.completeUploadExternal',
data: formDataComplete,
options: Options(
contentType: Headers.formUrlEncodedContentType,
),
);

_printLog(
'Server responded to completeUploadExternal with code: '
'${responseFileComplete.statusCode} '
'and message upload file: ${responseFileComplete.statusMessage}',
);

if (responseFileComplete.data == null ||
responseFileComplete.data['ok'] != true) {
return {};
}
_printLog(responseFileComplete.data['files'][0]['url_private']);

return {
'attachments': [
{
'image_url': responseFileComplete.data['files'][0]['url_private'],
'text': responseFileComplete.data['files'][0]['permalink'],
},
],
};
} catch (exception) {
_printLog('Failed to send screenshot: $exception');
return {};
}
}

String _buildMessage(Report report) {
final stringBuffer = StringBuffer()
..write('*Error:* ```${report.error}```\n');
Expand Down
3 changes: 2 additions & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: catcher_2
description: Plugin for error catching which provides multiple handlers for dealing with errors when they are not caught by the developer.
version: 1.2.6
version: 1.3.0
homepage: https://github.com/ThexXTURBOXx/catcher_2
repository: https://github.com/ThexXTURBOXx/catcher_2
issue_tracker: https://github.com/ThexXTURBOXx/catcher_2/issues
Expand All @@ -27,6 +27,7 @@ environment:
flutter: ">=3.0.0"

dependencies:
cross_file: ^0.3.0
device_info_plus: '>=9.0.0 <11.0.0'
dio: ^5.0.1
flutter:
Expand Down

0 comments on commit 4ab026c

Please sign in to comment.