slack 추가
This commit is contained in:
parent
560e0535e1
commit
f1559c7b07
5 changed files with 467 additions and 0 deletions
132
lib/utils/slack/slack_data.dart
Normal file
132
lib/utils/slack/slack_data.dart
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:dart_framework/utils/system_util.dart';
|
||||
import 'package:http/http.dart';
|
||||
// ignore: depend_on_referenced_packages, implementation_imports
|
||||
import 'package:http_parser/src/media_type.dart';
|
||||
part 'slack_data.g.dart';
|
||||
|
||||
enum SlackActionType { button, select }
|
||||
|
||||
enum SlackActionStyle { danger, primary }
|
||||
|
||||
@JsonSerializable()
|
||||
class DataSlack {
|
||||
String? message;
|
||||
late List<SlackAttachment> attachments;
|
||||
|
||||
String getAttachments() {
|
||||
var list = [];
|
||||
for (var item in attachments) {
|
||||
list.add(item.toJson());
|
||||
}
|
||||
return jsonEncode(list);
|
||||
}
|
||||
|
||||
DataSlack();
|
||||
|
||||
factory DataSlack.fromJson(Map<String, dynamic> json) =>
|
||||
_$DataSlackFromJson(json);
|
||||
Map<String, dynamic> toJson() => _$DataSlackToJson(this);
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
class SlackAttachment {
|
||||
String? fallback;
|
||||
String? color;
|
||||
String? author_name;
|
||||
String? author_link;
|
||||
String? author_icon;
|
||||
String? title;
|
||||
String? title_link;
|
||||
String? pretext;
|
||||
String? text;
|
||||
String? image_url;
|
||||
String? thumb_url;
|
||||
String? footer;
|
||||
String? footer_icon;
|
||||
String? ts;
|
||||
|
||||
List<SlackField>? fields;
|
||||
List<SlackAction>? actions;
|
||||
|
||||
SlackAttachment();
|
||||
|
||||
factory SlackAttachment.fromJson(Map<String, dynamic> json) =>
|
||||
_$SlackAttachmentFromJson(json);
|
||||
Map<String, dynamic> toJson() => _$SlackAttachmentToJson(this);
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
class SlackField {
|
||||
String? title;
|
||||
String? value;
|
||||
|
||||
SlackField();
|
||||
SlackField.withParam(this.title, this.value);
|
||||
|
||||
factory SlackField.fromJson(Map<String, dynamic> json) =>
|
||||
_$SlackFieldFromJson(json);
|
||||
Map<String, dynamic> toJson() => _$SlackFieldToJson(this);
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
class SlackAction {
|
||||
SlackActionType? type;
|
||||
String? name;
|
||||
String? text;
|
||||
String? url;
|
||||
String? value;
|
||||
SlackActionStyle? style;
|
||||
/*SlackField options;
|
||||
SlackConfirm confirm;*/
|
||||
|
||||
SlackAction();
|
||||
SlackAction.withParam(
|
||||
this.name, this.text, this.url, this.value, this.type, this.style);
|
||||
|
||||
factory SlackAction.fromJson(Map<String, dynamic> json) =>
|
||||
_$SlackActionFromJson(json);
|
||||
Map<String, dynamic> toJson() => _$SlackActionToJson(this);
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
class SlackConfirm {
|
||||
String? title;
|
||||
String? text;
|
||||
String? ok_text;
|
||||
String? dismiss_text;
|
||||
}
|
||||
|
||||
// To-do: File upload test
|
||||
@JsonSerializable()
|
||||
class SlackFile {
|
||||
String? title;
|
||||
String? initial_comment;
|
||||
String? filePath;
|
||||
String? fileName;
|
||||
String? mimeType;
|
||||
|
||||
Future<MultipartRequest> setFileData(MultipartRequest form) async {
|
||||
// var bytes = await readFileByte(filePath!);
|
||||
if (title != null) {
|
||||
form.fields['title'] = title!;
|
||||
}
|
||||
if (initial_comment != null) {
|
||||
form.fields['initial_comment'] = initial_comment!;
|
||||
}
|
||||
form.fields['filetype'] =
|
||||
"auto"; //참조 - https://api.slack.com/types/file#file_types
|
||||
if (fileName != null && mimeType != null) {
|
||||
form.files.add(MultipartFile.fromBytes(
|
||||
'file', await File.fromUri(Uri.parse(filePath!)).readAsBytes(),
|
||||
contentType: MediaType(HttpHeaders.contentTypeHeader, mimeType!)));
|
||||
}
|
||||
var c = Completer<MultipartRequest>();
|
||||
c.complete(form);
|
||||
return c.future;
|
||||
}
|
||||
}
|
||||
128
lib/utils/slack/slack_data.g.dart
Normal file
128
lib/utils/slack/slack_data.g.dart
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'slack_data.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
DataSlack _$DataSlackFromJson(Map<String, dynamic> json) => DataSlack()
|
||||
..message = json['message'] as String?
|
||||
..attachments = (json['attachments'] as List<dynamic>)
|
||||
.map((e) => SlackAttachment.fromJson(e as Map<String, dynamic>))
|
||||
.toList();
|
||||
|
||||
Map<String, dynamic> _$DataSlackToJson(DataSlack instance) => <String, dynamic>{
|
||||
'message': instance.message,
|
||||
'attachments': instance.attachments,
|
||||
};
|
||||
|
||||
SlackAttachment _$SlackAttachmentFromJson(Map<String, dynamic> json) =>
|
||||
SlackAttachment()
|
||||
..fallback = json['fallback'] as String?
|
||||
..color = json['color'] as String?
|
||||
..author_name = json['author_name'] as String?
|
||||
..author_link = json['author_link'] as String?
|
||||
..author_icon = json['author_icon'] as String?
|
||||
..title = json['title'] as String?
|
||||
..title_link = json['title_link'] as String?
|
||||
..pretext = json['pretext'] as String?
|
||||
..text = json['text'] as String?
|
||||
..image_url = json['image_url'] as String?
|
||||
..thumb_url = json['thumb_url'] as String?
|
||||
..footer = json['footer'] as String?
|
||||
..footer_icon = json['footer_icon'] as String?
|
||||
..ts = json['ts'] as String?
|
||||
..fields = (json['fields'] as List<dynamic>?)
|
||||
?.map((e) => SlackField.fromJson(e as Map<String, dynamic>))
|
||||
.toList()
|
||||
..actions = (json['actions'] as List<dynamic>?)
|
||||
?.map((e) => SlackAction.fromJson(e as Map<String, dynamic>))
|
||||
.toList();
|
||||
|
||||
Map<String, dynamic> _$SlackAttachmentToJson(SlackAttachment instance) =>
|
||||
<String, dynamic>{
|
||||
'fallback': instance.fallback,
|
||||
'color': instance.color,
|
||||
'author_name': instance.author_name,
|
||||
'author_link': instance.author_link,
|
||||
'author_icon': instance.author_icon,
|
||||
'title': instance.title,
|
||||
'title_link': instance.title_link,
|
||||
'pretext': instance.pretext,
|
||||
'text': instance.text,
|
||||
'image_url': instance.image_url,
|
||||
'thumb_url': instance.thumb_url,
|
||||
'footer': instance.footer,
|
||||
'footer_icon': instance.footer_icon,
|
||||
'ts': instance.ts,
|
||||
'fields': instance.fields,
|
||||
'actions': instance.actions,
|
||||
};
|
||||
|
||||
SlackField _$SlackFieldFromJson(Map<String, dynamic> json) => SlackField()
|
||||
..title = json['title'] as String?
|
||||
..value = json['value'] as String?;
|
||||
|
||||
Map<String, dynamic> _$SlackFieldToJson(SlackField instance) =>
|
||||
<String, dynamic>{
|
||||
'title': instance.title,
|
||||
'value': instance.value,
|
||||
};
|
||||
|
||||
SlackAction _$SlackActionFromJson(Map<String, dynamic> json) => SlackAction()
|
||||
..type = $enumDecodeNullable(_$SlackActionTypeEnumMap, json['type'])
|
||||
..name = json['name'] as String?
|
||||
..text = json['text'] as String?
|
||||
..url = json['url'] as String?
|
||||
..value = json['value'] as String?
|
||||
..style = $enumDecodeNullable(_$SlackActionStyleEnumMap, json['style']);
|
||||
|
||||
Map<String, dynamic> _$SlackActionToJson(SlackAction instance) =>
|
||||
<String, dynamic>{
|
||||
'type': _$SlackActionTypeEnumMap[instance.type],
|
||||
'name': instance.name,
|
||||
'text': instance.text,
|
||||
'url': instance.url,
|
||||
'value': instance.value,
|
||||
'style': _$SlackActionStyleEnumMap[instance.style],
|
||||
};
|
||||
|
||||
const _$SlackActionTypeEnumMap = {
|
||||
SlackActionType.button: 'button',
|
||||
SlackActionType.select: 'select',
|
||||
};
|
||||
|
||||
const _$SlackActionStyleEnumMap = {
|
||||
SlackActionStyle.danger: 'danger',
|
||||
SlackActionStyle.primary: 'primary',
|
||||
};
|
||||
|
||||
SlackConfirm _$SlackConfirmFromJson(Map<String, dynamic> json) => SlackConfirm()
|
||||
..title = json['title'] as String?
|
||||
..text = json['text'] as String?
|
||||
..ok_text = json['ok_text'] as String?
|
||||
..dismiss_text = json['dismiss_text'] as String?;
|
||||
|
||||
Map<String, dynamic> _$SlackConfirmToJson(SlackConfirm instance) =>
|
||||
<String, dynamic>{
|
||||
'title': instance.title,
|
||||
'text': instance.text,
|
||||
'ok_text': instance.ok_text,
|
||||
'dismiss_text': instance.dismiss_text,
|
||||
};
|
||||
|
||||
SlackFile _$SlackFileFromJson(Map<String, dynamic> json) => SlackFile()
|
||||
..title = json['title'] as String?
|
||||
..initial_comment = json['initial_comment'] as String?
|
||||
..filePath = json['filePath'] as String?
|
||||
..fileName = json['fileName'] as String?
|
||||
..mimeType = json['mimeType'] as String?;
|
||||
|
||||
Map<String, dynamic> _$SlackFileToJson(SlackFile instance) => <String, dynamic>{
|
||||
'title': instance.title,
|
||||
'initial_comment': instance.initial_comment,
|
||||
'filePath': instance.filePath,
|
||||
'fileName': instance.fileName,
|
||||
'mimeType': instance.mimeType,
|
||||
};
|
||||
188
lib/utils/slack/slack_sender.dart
Normal file
188
lib/utils/slack/slack_sender.dart
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:color/color.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
import 'slack_data.dart';
|
||||
|
||||
enum SlackBuildType { android, ios, windows, mac, web }
|
||||
|
||||
class SlackSender {
|
||||
//Bot TOKEN
|
||||
final token = "xoxb-291006091297-QT2K1PCpwyX4b5wKk3AQew2G";
|
||||
//API - https://api.slack.com/methods/chat.postMessage
|
||||
final urlMessagePost = "https://slack.com/api/chat.postMessage";
|
||||
//API - https://api.slack.com/methods/files.upload
|
||||
final urlFileUplaod = "https://slack.com/api/files.upload";
|
||||
|
||||
Function(String)? _completeListener;
|
||||
Function(Exception)? _errorListener;
|
||||
|
||||
String getValidatedUser(String? targetUser) {
|
||||
targetUser ??= "@channel";
|
||||
if (targetUser.indexOf("@") != 0) targetUser = "@$targetUser";
|
||||
return targetUser;
|
||||
}
|
||||
|
||||
String getURL(String channel, DataSlack message, String? targetUser,
|
||||
{bool includeURL = true}) {
|
||||
targetUser = getValidatedUser(targetUser);
|
||||
var buffer = StringBuffer();
|
||||
if (includeURL) {
|
||||
buffer.write(urlMessagePost);
|
||||
buffer.write('?');
|
||||
}
|
||||
buffer.write('token=$token');
|
||||
buffer.write('&channel=$channel');
|
||||
var sendMessage = message.message == null
|
||||
? ''
|
||||
: Uri.encodeComponent(' ${message.message!}');
|
||||
buffer.write('&text=$targetUser$sendMessage');
|
||||
buffer
|
||||
.write('&attachments=${Uri.encodeComponent(message.getAttachments())}');
|
||||
buffer.write('&link_names=true');
|
||||
print(buffer.toString());
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
void onComplete(String result) {
|
||||
print(result);
|
||||
if (_completeListener != null) _completeListener!(result);
|
||||
}
|
||||
|
||||
void onError(Exception exception) {
|
||||
if (_errorListener != null) _errorListener!(exception);
|
||||
}
|
||||
|
||||
void initializeListener() {
|
||||
_completeListener = null;
|
||||
_errorListener = null;
|
||||
}
|
||||
|
||||
//단일 Attachment로 보낼시
|
||||
Future send(String channel, SlackAttachment attachment,
|
||||
{String? targetUser,
|
||||
Function(String)? completeListener,
|
||||
Function(Exception)? errorListener}) async {
|
||||
var data = DataSlack();
|
||||
data.attachments = [attachment];
|
||||
return await sends(channel, data, targetUser: targetUser);
|
||||
}
|
||||
|
||||
//복수의 Attachment로 보낼시
|
||||
Future sends(String channel, DataSlack message,
|
||||
{String? targetUser,
|
||||
Function(String)? completeListener,
|
||||
Function(Exception)? errorListener}) async {
|
||||
var c = Completer();
|
||||
String url = getURL(channel, message, targetUser);
|
||||
|
||||
var response = await http.get(Uri.parse(url));
|
||||
|
||||
print(response.body);
|
||||
if (_completeListener != null) _completeListener!(response.body);
|
||||
|
||||
c.complete();
|
||||
return c.future;
|
||||
}
|
||||
|
||||
String getParameters(String channel, DataSlack message,
|
||||
{String? targetUser}) {
|
||||
return getURL(channel, message, targetUser, includeURL: false);
|
||||
}
|
||||
|
||||
/// ****************** File Send *******************
|
||||
http.MultipartRequest getPostForm(
|
||||
String url, String channel, String? targetUser) {
|
||||
var form = http.MultipartRequest('POST', Uri.parse(url));
|
||||
targetUser = getValidatedUser(targetUser);
|
||||
form.fields['token'] = token;
|
||||
form.fields['channels'] = channel;
|
||||
return form;
|
||||
}
|
||||
|
||||
void sendFile(String channel, SlackFile file,
|
||||
{String? targetUser,
|
||||
Function(String)? completeListener,
|
||||
Function(Exception)? errorListener}) async {
|
||||
_completeListener = completeListener;
|
||||
_errorListener = errorListener;
|
||||
var form = getPostForm(urlFileUplaod, channel, targetUser);
|
||||
form = await file.setFileData(form);
|
||||
//To-do: send http
|
||||
}
|
||||
|
||||
/// ****************** Send template *******************
|
||||
void sendTest() async {
|
||||
DataSlack message = DataSlack();
|
||||
message.attachments = [];
|
||||
var attachment = SlackAttachment();
|
||||
attachment.author_name = 'toki';
|
||||
attachment.text = 'This is Test!!';
|
||||
attachment.color = '#ff2f00';
|
||||
attachment.fields = [
|
||||
SlackField.withParam('Test1', 'Value1'),
|
||||
SlackField.withParam('Test2', 'Value2'),
|
||||
];
|
||||
message.attachments.add(attachment);
|
||||
|
||||
attachment = SlackAttachment();
|
||||
attachment.author_name = 'toki';
|
||||
attachment.text = 'This is Test!!2';
|
||||
attachment.color = '#0084ff';
|
||||
attachment.fields = [
|
||||
SlackField.withParam('Test3', 'Value3'),
|
||||
SlackField.withParam('Test4', 'Value4'),
|
||||
];
|
||||
message.attachments.add(attachment);
|
||||
sends('app_notifier', message, targetUser: 'toki');
|
||||
}
|
||||
|
||||
Future sendBuild(String appName, SlackBuildType buildType, Color color,
|
||||
String version, String downloadURL,
|
||||
{String channel = 'build'}) async {
|
||||
final buildURL = 'http://toki-labs.com/build/';
|
||||
String thumUrl = buildType == SlackBuildType.android
|
||||
? '${buildURL}android.png'
|
||||
: '${buildURL}appstore.png';
|
||||
String projectIcon = "$buildURL$appName/icon.png";
|
||||
String emoji =
|
||||
buildType == SlackBuildType.android ? ":android1: " : ":ios0: ";
|
||||
String osColor = buildType == SlackBuildType.ios ? "#ED145B" : "#81CA3F";
|
||||
var message = DataSlack();
|
||||
message.attachments = [];
|
||||
var attachments = message.attachments;
|
||||
|
||||
var attachment = SlackAttachment();
|
||||
attachment.color = color.toHexColor().toCssString();
|
||||
attachment.fields = [SlackField.withParam(toPascal(appName), null)];
|
||||
attachments.add(attachment);
|
||||
|
||||
var buildTypeStr = buildTypeToString(buildType);
|
||||
attachment = SlackAttachment();
|
||||
attachment.color = osColor;
|
||||
attachment.author_name = /*emoji +*/ buildTypeStr;
|
||||
attachment.author_icon = thumUrl;
|
||||
attachment.thumb_url = projectIcon;
|
||||
attachment.fields = [
|
||||
SlackField.withParam(null,
|
||||
"Successfully build $buildTypeStr binary file.\nBuild version: *$version*")
|
||||
];
|
||||
attachment.actions = [
|
||||
SlackAction.withParam(
|
||||
'build', 'Download', downloadURL, null, SlackActionType.button, null)
|
||||
];
|
||||
attachments.add(attachment);
|
||||
|
||||
return await sends(channel, message);
|
||||
}
|
||||
|
||||
String buildTypeToString(SlackBuildType type) {
|
||||
var typeStr = type.toString().replaceAll('SlackBuildType.', '');
|
||||
return toPascal(typeStr);
|
||||
}
|
||||
|
||||
String toPascal(String raw) {
|
||||
return '${raw[0].toUpperCase()}${raw.substring(1).toLowerCase()}';
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:yaml/yaml.dart';
|
||||
|
||||
|
|
@ -87,3 +88,19 @@ Future get simpleFuture {
|
|||
c.complete();
|
||||
return c.future;
|
||||
}
|
||||
|
||||
Future<Uint8List?> readFileByte(String filePath) async {
|
||||
var c = Completer<Uint8List>();
|
||||
Uri myUri = Uri.parse(filePath);
|
||||
File audioFile = File.fromUri(myUri);
|
||||
Uint8List? bytes;
|
||||
await audioFile.readAsBytes().then((value) {
|
||||
bytes = Uint8List.fromList(value);
|
||||
print('reading of bytes is completed');
|
||||
}).catchError((onError) {
|
||||
print(
|
||||
'Exception Error while reading audio from path:' + onError.toString());
|
||||
});
|
||||
c.complete(bytes);
|
||||
return c.future;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ dependencies:
|
|||
archive: ^3.3.5
|
||||
ftpconnect: ^2.0.5
|
||||
yaml: ^3.1.1
|
||||
http: ^0.13.5
|
||||
color: ^3.0.0
|
||||
|
||||
dev_dependencies:
|
||||
lints: ^2.0.0
|
||||
|
|
|
|||
Loading…
Reference in a new issue