// GraalJS Script (engine type: 2)
//////// START "main()" /////////////////////////////////////////////////////////////////
main();
function main(){
//// == Config Retrieving / 工程コンフィグの参照 ==
const strAuthzSetting = configs.get ( "AuthzConfU" ); /// REQUIRED
engine.log( " AutomatedTask Config: Authz Setting: " + strAuthzSetting );
const strSlackChannel = configs.get ( "StrConfA1" ); /// REQUIRED
if( strSlackChannel === "" ){
throw new Error( "\n AutomatedTask ConfigError:" +
" Config {A1: SlackChannel} is empty \n" );
}
const strThread = configs.get ( "StrConfA2" ); // NotRequired
if( strThread !== "" ){
engine.log( " AutomatedTask ConfigWarning:" +
" Message is posted in thread " + strThread );
}
const strToUserId = configs.get ( "StrConfA3" ); // NotRequired
if( strToUserId !== "" ){
engine.log( " AutomatedTask ConfigWarning:" +
" Message is posted with Ephemeral mode " + strToUserId );
}
const strMessageTitle = configs.get ( "StrConfB1" ); /// REQUIRED
if( strMessageTitle === "" ){
throw new Error( "\n AutomatedTask ConfigError:" +
" Config {B1: Title} is empty \n" );
}
const strMessageBody = configs.get ( "StrConfC1" ); /// REQUIRED
if( strMessageBody === "" ){
throw new Error( "\n AutomatedTask ConfigError:" +
" Config {C1: Message} is empty \n" );
}
const strTwocolTsv = configs.get ( "StrConfC2" ); // NotRequired
const strCategoryImgUrl = configs.get ( "StrConfC3" ); // NotRequired
const strPocketMessageTs = configs.getObject( "SelectConfD1" ); // NotRequired
//// == Data Retrieving / ワークフローデータの参照 ==
// (Nothing. Some workflow data is referenced via Expression Language in Config.)
//// == Calculating / 演算 ==
/// TSV, Check Empty
let strKeyAndValue = "";
if( strTwocolTsv !== "" ){
let arrRows = strTwocolTsv.split("\n");
engine.log( " AutomatedTask Config:" +
" TSV Rows " + arrRows.length );
for( let i = 0; i < arrRows.length; i++ ){
let arrCells = arrRows[i].split("\t");
if( arrCells.length < 2 ){ // load stop with end line or illegal line
engine.log( " AutomatedTask UnexpectedTsv line: " + i );
break;
}
for( let j = 0; j < 2; j++ ){
if( arrCells[j] === "" ){
strKeyAndValue += "n/a";
}else{
strKeyAndValue += arrCells[j];
}
strKeyAndValue += "\n";
}
if( i === 4 ){ break; } // up to 5
}
strKeyAndValue = strKeyAndValue.slice( 0, -1 );
}
/// Post Message
/// Slack API
/// https://api.slack.com/methods/chat.postMessage
/// https://api.slack.com/methods/chat.postEphemeral
/// https://api.slack.com/messaging/sending
/// https://api.slack.com/reference/messaging/payload
/// https://api.slack.com/reference/block-kit/blocks
// request1, prepare
let request1Obj = {};
request1Obj.channel = strSlackChannel;
if( strToUserId !== "" ){
request1Obj.user = strToUserId;
}
request1Obj.text = strMessageTitle + "\n" + strMessageBody;
if( strThread !== "" ){
request1Obj.thread_ts = strThread;
}
request1Obj.blocks = [];
request1Obj.blocks[0] = {};
request1Obj.blocks[0].type = "header";
request1Obj.blocks[0].text = {};
request1Obj.blocks[0].text.type = "plain_text";
request1Obj.blocks[0].text.text = strMessageTitle.slice(0,150); // MAX 150
// https://api.slack.com/reference/block-kit/blocks#header
request1Obj.blocks[0].text.emoji = true; // No format 'false'
request1Obj.blocks[1] = {};
request1Obj.blocks[1].type = "section";
request1Obj.blocks[1].text = {};
request1Obj.blocks[1].text.type = "mrkdwn"; // if "plain_text", newline \n not work.
request1Obj.blocks[1].text.text = strMessageBody.slice(0,3000); // MAX 3000
// https://api.slack.com/reference/block-kit/blocks#section
if( strCategoryImgUrl !== "" ){
request1Obj.blocks[1].accessory = {};
request1Obj.blocks[1].accessory.type = "image";
request1Obj.blocks[1].accessory.image_url = strCategoryImgUrl;
request1Obj.blocks[1].accessory.alt_text = "CategoryImg";
}
if( strKeyAndValue !== "" ){
let arrKeyAndValue = strKeyAndValue.split("\n");
request1Obj.blocks[1].fields = [];
for( let i = 0; i < arrKeyAndValue.length; i++ ){
request1Obj.blocks[1].fields[i] = {};
request1Obj.blocks[1].fields[i].type = "mrkdwn"; // No format "plain_text"
request1Obj.blocks[1].fields[i].text = arrKeyAndValue[i];
}
}
let request1Uri = "https://slack.com/api/chat.postMessage";
if( strToUserId !== "" ){
request1Uri = "https://slack.com/api/chat.postEphemeral";
}
let request1 = httpClient.begin(); // HttpRequestWrapper
request1 = request1.authSetting( strAuthzSetting ); // with "Authorization: Bearer XX"
// https://questetra.zendesk.com/hc/en-us/articles/360024574471-R2300#HttpRequestWrapper
request1 = request1.body( JSON.stringify( request1Obj ), "application/json" );
// request1, try
const response1 = request1.post( request1Uri ); // HttpResponseWrapper
engine.log( " AutomatedTask ApiRequest1 Start: " + request1Uri );
const response1Code = response1.getStatusCode() + "";
const response1Body = response1.getResponseAsString() + "";
engine.log( " AutomatedTask ApiResponse Status: " + response1Code );
if( response1Code !== "200"){
throw new Error( "\n AutomatedTask UnexpectedResponseError: " +
response1Code + "\n" + response1Body + "\n" );
}
// response1, parse
/*
engine.log( response1Body ); // debug
{
"ok":true,
"channel":"C029A930R",
"ts":"1622607940.003700",
"message":{
"bot_id":"B02471SR3BJ",
"type":"message",
# # #
"ts":"1622607940.003700",
"team":"T029A930F",
"bot_profile":{
# # #
},
}
}
== ephemeral ==
{
"ok":true,
"message_ts":"1622179598.000100"
}
== thread ==
{
"ok":true,
"channel":"C029A930R",
"ts":"1622606758.003500",
"message":{
"bot_id":"B02471SR3BJ",
"type":"message",
# # #
"ts":"1622606758.003500",
# # #
"bot_profile":{
# # #
},
"blocks":[
# # #
],
"thread_ts":"1622602402.002700",
# # #
}
}
*/
const response1Obj = JSON.parse( response1Body );
if( response1Obj.ok !== true ){
throw new Error( "\n AutomatedTask UnexpectedResponseError: " +
response1Code + "\n" + response1Body + "\n" );
}
if( response1Obj.hasOwnProperty('warning') ){
engine.log( " AutomatedTask ApiResponse Warning: " + response1Obj.warning );
}
//// == Data Updating / ワークフローデータへの代入 ==
if( strPocketMessageTs !== null ){
engine.setData( strPocketMessageTs, response1Obj.ts );
}
} //////// END "main()" /////////////////////////////////////////////////////////////////
/*
Notes:
- Posts to the business communication tool "Slack".
- Posted via "Slack App".
- Can post not only on public channels but also on private channels.
- Automate postings with business data inserted.
- e.g. Placed before the approval step, automatically notifies "arrival of approval".
- e.g. Placed after the timer start, announces the "expense application deadline".
- e.g. Notifies the long URL of Receive-Task.
- Messages are posted in Block format.
- The message posted by this Addon consists of a title (header) block and a body (section) block.
- It is also possible to attach key-value (2-col TSV) information to the body block.
- Up to the 5th row is displayed.
- Data that is not tab-delimited will be ignored.
- Ignored after the 3rd column and 6th row.
- Empty cells show "n/a".
- You can use mrkdwn notation or colon emoji notation for the string part.
- https://api.slack.com/reference/surfaces/formatting#basics
- "*bold*" will produce bold text
- "~strike~" will produce strikethrough text
- "_italic_" will produce italicized text
- https://www.webfx.com/tools/emoji-cheat-sheet/
- URLs to refer to the process in the workflow
- "<${var[applicationRoot]}PE/Workitem/list?processInstanceId=#{processInstanceId}|Desktop View>"
- "<${var[applicationRoot]}SP/PE/ProcessInstance/view?processInstanceId=#{processInstanceId}|Mobile View>"
- The channel is specified by the channel name (eg "#general") or ID (eg "C1234567890").
- To reduce the risk of trouble, set an ID that is an invariant value.
- You need to create a "Slack app" in advance.
- For new creation, [Settings and Management] > [Manage Apps] > [Build] > "Create New App"
- https://api.slack.com/apps/
- Setting Example
- (Wizard)
- App Name: "BPMS Notification"
- Pick a workspace to develop your app in: (Select Your Org)
- Basic Information
- Display Information: (Add Bot-Logo)
- OAuth & Permissions
- Bot Token Scopes: Added "chat:write" (and "files:write")
- Click "Install to Workfpace" to get "Bot User OAuth Token"
- You need to add "Slack App" to your workspace in advance.
- Bottom left of workspace, [Add apps]
- You need to add "Slack app" to each channel in advance.
- Upper right of each channel, [Details] > [More] > [Add apps]
Notes-ja:
- ビジネス・コミュニケーションツール「Slack」に対して投稿します。
- "Slackアプリ" を経由して投稿されます。
- パブリックチャンネルだけでなく、プライベートチャンネル等にも投稿できます。
- 業務データが挿し込まれたメッセージが自動的に投稿されるようになります。
- たとえば、決裁工程の前に配置し "決裁案件の到達" が自動通知されるように設定します。
- たとえば、タイマー開始の直後に配置し "経費申請の締切日" が定時アナウンスされるように設定します。
- たとえば、"待受工程(受信タスク)のLongURL" が Slackにも通知されるように設定します。
- 投稿メッセージはBlock形式にて投稿されます。
- このAddonによる投稿は、タイトル(header)ブロックと本文ブロック(section)で構成されます。
- 本文ブロックに、Key-Value 情報(2列TSV)を添付することも可能です。
- 5行目まで表示されます。
- タブ区切りでないデータは無視されます。
- 3列目以降および6行目以降は無視されます。
- 空のセルには "n/a" が表示されます。
- 文字列部には mrkdwn 記法や colon emoji 記法を利用できます。
- https://api.slack.com/reference/surfaces/formatting#basics
- "*ボールド*" 太文字
- "~ストライク~" 取り消し線文字列
- "_イタリック_" 斜体文字列 (日本語フォントの多くは判別できません)
- https://www.webfx.com/tools/emoji-cheat-sheet/
- ワークフローに流れるプロセスを参照するためのURL
- "<${var[applicationRoot]}PE/Workitem/list?processInstanceId=#{processInstanceId}|WFで見る Desktop View>"
- "<${var[applicationRoot]}SP/PE/ProcessInstance/view?processInstanceId=#{processInstanceId}|WFで見る Mobile View>"
- チャンネルは、チャンネル名(例 "#general")もしくはID(例 "C1234567890")で指定します。
- トラブルリスクを低減するためには、不変値であるIDを設定します。
- あらかじめ "Slackアプリ" を作成しておく必要があります。
- 新規作成の場合 [設定と管理] > [アプリを管理する] > [ビルド] から "Create New App" を選択します。
- https://api.slack.com/apps/
- 設定例
- (Wizard)
- App Name: "BPMS Notification"
- Pick a workspace to develop your app in: (所属組織のワークスペースを選択)
- Basic Information
- Display Information: (ボットロゴを追加)
- OAuth & Permissions
- Bot Token Scopes: "chat:write" (と "files:write") を追加
- Install to Workfpace をクリックし、"Bot User OAuth Token" を取得
- あらかじめワークスペースに "Slackアプリ" を追加しておく必要があります。
- ワークスペース左下 [アプリを追加する]
- あらかじめ各チャンネルに "Slackアプリ" を追加しておく必要があります。
- 各チャンネル画面の右上 [チャンネル詳細を開く] > [その他] > [アプリを追加する]
APPENDIX-en
- Setting example of "HTTP Authentication"
- "Token Fixed Value"
- Name: NotificatonToSlack
- Token: {Bot User OAuth Token}
- Also possible to post an "Ephemeral Message" that is displayed only to that user.
- Recipients can see the "Only visible to you".
- The background color of the post will be gray.
- Ephemeral Message is not persistent between terminals or sessions.
- If you do something, the message will disappear.
- The user must be on the specified channel.
- SLACK USER is specified by the ID of the message receiving user.
- UserID (Member ID) can be obtained from the details of "Profile" and URL.
- Ephemeral Messages cannot be threaded.
- For the thread_ts of the thread post, specify the time stamp (ts) of the parent message.
- Thread posting to the time stamp (ts) of the child message is not possible.
- No error will occur on API communication, but it will not be displayed.
- If the title is set to exceed 150 characters, the 151st and subsequent characters will not be sent.
- The category image URL must be public.
- For more information on how to use Slack itself, refer to the Slack documentation.
- Token Types
- https://api.slack.com/authentication/token-types
- Scopes and permissions
- https://api.slack.com/scopes
- https://api.slack.com/scopes/chat:write
- https://api.slack.com/scopes/files:write
- API Methods
- https://api.slack.com/methods
- https://api.slack.com/methods/chat.postMessage
- https://api.slack.com/methods/chat.postEphemeral
APPENDIX-ja
- "HTTP認証" の設定例
- "トークン直接指定"
- 名前: NotificatonToSlack
- トークン: {Bot User OAuth Token}
- ユーザIDを指定して、当該ユーザにのみ表示される「揮発メッセージ」を投稿することも可能です。
- 受信者は「あなただけに表示されています」の表示を確認できます。
- 投稿の背景色がグレーになります。
- 揮発メッセージ(Ephemeral Message)は、端末間やセッション間等において永続されません。
- 何らかの操作をすれば、メッセージは表示されなくなります。
- ユーザは指定されたチャンネルにいなければなりません。
- SLACK USER はメッセージ受信ユーザのIDで指定します。
- UserID(メンバーID)は「プロフィール」の詳細やURL等から取得できます。
- 揮発メッセージは、スレッドを取れません。
- スレッド投稿のts値(thread_ts)には、親メッセージのタイムスタンプ(ts)を指定します。
- 子メッセージのタイムスタンプ(ts)に対するスレッド投稿はできません。
- API通信上はエラーにはなりませんが、表示されません。
- タイトルが150文字を超えて設定された場合、151文字目以降は送信されません。
- カテゴリ画像のURLは、パブリックなURLでなければなりません。
- Slack の詳細仕様は、Slack のドキュメントを参照してください。
- Token Types
- https://api.slack.com/authentication/token-types
- Scopes and permissions
- https://api.slack.com/scopes
- API Methods
- https://api.slack.com/methods
- https://api.slack.com/methods/chat.postMessage
- https://api.slack.com/methods/chat.postEphemeral
*/