RACCOON TECH BLOG

株式会社ラクーンホールディングス 技術戦略部より、
tipsやノウハウなど技術的な話題を
発信いたします。

Visual Studio CodeでSlackに投稿する拡張機能をDIY

こんにちは。開発チームの阿部です。

前回、VS Codeの拡張機能を作成する記事を書かせて頂きました。
今回はVS Code拡張機能作成の第二弾で、VS Codeの拡張機能+SlackのAPIを使ってメッセージ投稿用の拡張機能を作成したいと思います。VS Codeには既にいくつかSlackの拡張機能が存在していますが、SlackのAPIの事も学びたいと考え今回拡張機能をDIYしようと思いました。
※Slackの詳しい事については下記のリンクを参照してください。
VS Codeはテキストエディタなので、メモやソースコードの内容を共有したい場合にファイルの内容をコピペをして内容を送信する事よりもVS Code上からそのまま共有したい内容を送信できたほうが簡単で手間が省けます。また一時的にソースコードを共有したい場合にソースにシンタックスハイライト(Slackの機能)が付いていると見やすく読み手に親切です。開発に関してもSlackに投稿用のAPIが用意されているのでそれを利用して拡張機能に組み込めば簡単に投稿が出来ます。これらが今回拡張機能DIYをしようと思ったきっかけです。

セットアップについて

VS Code拡張機能作成に必要な環境とセットアップ、拡張機能作成時の注意点などは前回の記事をご覧ください。
Visual Studio CodeがRSSリーダになる?VS Codeの拡張機能を作れば作業が捗る

Slackの公式ページ・概要をリンクして置きました。
良かったら一度見てみてください。
Slackって何 公式 日本語
Slack公式 日本語

Slackの公式ページから必要な設定手順

SlackのTokenを取得する

メッセージ投稿をする為に必要なTokenを取得する為の設定をしていきます。
slack APIにログインをします。

ログインしたらYour Apps のCreate an Appをクリックします。

slack_0

押すとApp Name workspaceを選択する画面が出てきますのでapp名を入力しworkspaceを選択します。
Create Appを押下します。
※選択したDevelopment Slack Workspaceに入力したapp名が既にある場合は、適宜変更してください。
slack_1

画面が切り替わったら、Permissionを選択します。

slack_2

Scope内のSelect Permission Scopesの項目に設定を追加します。
ここで設定する内容は、自分自身(USER)としてチャンネルにメッセージを投稿と自分自身(USER)としてチャンネルにファイルを投稿の2つをしたいので、Send message as use と Upload and modify files as user の許可を追加します。
Save Changes を押下します。
slack_3

ページの上の方に戻り
OAuth Tokens & Redirect URLs の Install App to Workspaceを押します。
slack_4

インストールの確認画面が出てきたら許可をします。
slack_5

画面が切り替わったらOAuth Access Token に表示されているTokenをコピーします。
slack_6

xoxp-xxxxxx-xxxxxxxx-xxxxxx-xxxxxxxx
※このTokenは外部に漏れてしまうと第三者など誰でも投稿できるようになってしまうので、
絶対に漏れないように厳重に管理してください。

Tokenでメッセージを投稿できるかを確認する。

取得したTokenでリクエストしてみます。ブラウザのアドレスバーに下記のURLを入れて確認してください。

https://slack.com/api/chat.postMessage?token=xoxp-xxxxxx-xxxxxxxx-xxxxxx-xxxxxxxx&channel=xxxxx&text=test&as_user=true

※token=xoxp-xxxxxx-xxxxxxxx-xxxxxx-xxxxxxxxは先ほど取得したものに読み替えてください。
※channelのIDの取得はSlackのデスクトップアプリをインストール済みであれば投稿したいチャンネル名を右クリック→リンクをコピーしてどこかに貼り付けます。

https://raccoon-co.slack.com/messages/abcdefg

のabcdefgがチャンネルIDです。
ブラウザ版Slackの方は開いているページのアドレスバーをご確認ください。

投稿をするとブラウザにはJSONが返却されます。

{"ok":true,"channel":"abcdef","ts":"1530254275.000168","message":{"type":"message","user":"ユーザーID","text":"test","bot_id":"ボットID","ts":"1530254275.000168"}}

投稿したSlackのチャンネルを確認し自分自身の投稿でtestと表示されていれば成功です。
次はこのTokenとSlack APIを使ってVS Codeの拡張機能を作成していきます。

実際に拡張機能を作成する

VS Codeの拡張機能作成の作成手順等は前回の記事「実際にextensionを作成する」の辺りをご覧ください。

テキストを送信する処理とコマンドを登録

\src\extension.tsにはテキストを投稿する処理を記述します。
今回はあらかじめ、設定ファイルから必要な設定を取得するようにしています。

export function activate(context: vscode.ExtensionContext) {
    let slackSendText = vscode.commands.registerCommand('extension.slackSendText', () => {
        const config = vscode.workspace.getConfiguration('slack');
        const url = config.general.postMessageUrl;
        const token = config.general.token;
        const channel = config.general.channel;
        let options = {
            url: url,
            form: {
                token: token,
                as_user: true,
                channel: channel,
                text: getText(vscode.window.activeTextEditor)
            }
        }
        sendMessage(options);
    });
    // エディタのテキストを取得する
    function getText(editor: any) {
        // editorの選択開始位置と終了位置が同じだったら未選択として開いているドキュメントの内容を全て取得する。
        if(editor.selection.start.line == editor.selection.end.line && editor.selection.start.character == editor.selection.end.character) {
            return editor.document.getText();
        } else {
            return editor.document.getText(new vscode.Range(editor.selection.start, editor.selection.end));
        }
    }
    // 投稿する処理
    function sendMessage(options: any) {
        const request = require('request');
        request.post(options, (error: any, response: any, body: any) => {
            console.log(response);
        })
    }
    context.subscriptions.push(slackSendText);
}

\package.json にコマンドの登録を行います。
今回はVS Codeのコマンドパレットから実行をするのではなく、マウスの右クリックをした時のコンテキストメニューからコマンドを実行したかったので
editor/contextにも記述をします。

    "activationEvents": [
        "onCommand:extension.slackSendText",
    ],
    "main": "./out/extension",
    "contributes": {
        "commands": [
            {
                "command": "extension.slackSendText",
                "title": "Slackにtextを送信"
            }
        ],
        "menus": {
            "editor/context": [
                {
                    "command": "extension.slackSendText"
                }
            ]
        },
        "configuration": {
            "type": "object",
            "title": "slack configration",
            "properties": {
                "slack.general": {
                    "type": "object",
                    "default": {
                        "postMessageUrl": "https://slack.com/api/chat.postMessage",
                        "filesUploadUrl": "https://slack.com/api/files.upload",
                        "channel": "投稿したいチャンネルID",
                        "token": "取得したToken"
                    },
                    "description": "slack extension"
                }
            }
        }
    }

では一度開発用のVS Codeから実行してみます。
デバックウィンドウが起動したら適当なファイルを開くか新規作成をして送りたいテキストを選択し右クリックをしてみます。

vs_0

commandsのtitle に設定した「Slackにtextを送信」が表示される事を確認しクリックをします。
Slackのチャンネルに選択した範囲のテキストが投稿されている事を確認してください。

ファイルアップロードに送る処理

スニペットとしてSlackに送信するにはテキストで送る時URLとは異なり、リクエストするパラメータが変わります。
active関数内にアップロード処理を追記します。
リクエスト可能なパラメータについては詳しくは下記をご覧ください。
slack API files.upload

let slackSendSnippet = vscode.commands.registerCommand('extension.slackSendSnippet', () => {
    const config = vscode.workspace.getConfiguration('slack');
    const url = config.general.filesUploadUrl;
    const token = config.general.token;
    const channel = config.general.channel;
    let options = {
        url: url,
        form: {
            token: token,
            as_user: true,
            channels: channel,
            content: getText(vscode.window.activeTextEditor),
            filetype: "auto"
        }
    }
    sendMessage(options);
});

slackSendSnippetのコマンドを\package.json に登録します。

    "activationEvents": [
        "onCommand:extension.slackSendText",
        "onCommand:extension.slackSendSnippet"
    ],
    "main": "./out/extension",
    "contributes": {
        "commands": [
            {
                "command": "extension.slackSendText",
                "title": "Slackにtextを送信"
            },
            {
                "command": "extension.slackSendSnippet",
                "title": "Slackにsnippetを送信"
            }
        ],
        "menus": {
            "editor/context": [
                {
                    "command": "extension.slackSendText"
                },
                {
                    "command": "extension.slackSendSnippet"
                }
            ]
        }
    }

デバックウィンドウをリロードして再度、適当なファイルを開くか新規作成をして送りたいテキストを選択し右クリックをしてみます。
vs_1

先ほどと同じようにcommandsのtitle に設定した「Slackにsnippetを送信」が表示される事を確認しクリックをします。
Slackに選択した範囲のテキストが指定チャンネルにファイルアップロードされている事を確認してください。

ナビゲーターでも送れるようにする

ナビゲーターを押してもメッセージが投稿できるようにします。
ナビゲーターとはVS Codeの上部にあるアイコン一覧の部分の事です。
vs_x_1

アイコンの画像を追加するとこの様な感じになります。
vs_x_2

※画像アイコンで表示した場合は、resourcesフォルダを作成してください。dark・lightフォルダを作成し画像を用意します。今回はslack_t.png slack_s.png という画像ファイルを2つ用意しました。
vs_x_3

使用した画像はhttp://yajidesign.com/こちらのサイト様からDLさせて頂きました。
VS Codeで表示する際に小さくリサイズされるので、アイコンの画像サイズは小さい方が潰れにくく見やすいです。SVGファイルを作成できる方はSVG画像を使用した方がより綺麗に表示できます。

またナビゲーターを誤って押してしまった時に勝手に投稿されてしまうのは困るので、
ナビゲーターが押された時は確認メッセージを出すように修正します。
vs_3

\package.json にナビゲーター用の設定を追加します。

    "activationEvents": [
        "onCommand:extension.slackSendText",
        "onCommand:extension.slackSendSnippet",
        "onCommand:extension.slackSendTextConfirm",
        "onCommand:extension.slackSendSnippetConfirm"
    ],
    "main": "./out/extension",
    "contributes": {
        "commands": [
            {
                "command": "extension.slackSendTextConfirm",
                "title": "Slackにtextを送信",
                "icon": {
                    "light": "resources/light/slack_t.png",
                    "dark": "resources/dark/slack_t.png"
                }
            },
            {
                "command": "extension.slackSendSnippetConfirm",
                "title": "Slackにsnippetを送信",
                "icon": {
                    "light": "resources/light/slack_s.png",
                    "dark": "resources/dark/slack_s.png"
                }
            },
            {
                "command": "extension.slackSendText",
                "title": "Slackにtextを送信"
            },
            {
                "command": "extension.slackSendSnippet",
                "title": "Slackにsnippetを送信"
            }
        ],
        "menus": {
            "editor/context": [
                {
                    "command": "extension.slackSendText"
                },
                {
                    "command": "extension.slackSendSnippet"
                }
            ],
            "editor/title": [
                {
                    "command": "extension.slackSendTextConfirm",
                    "alt": "extension.slackSendTextConfirm",
                    "group": "navigation"
                },
                {
                    "command": "extension.slackSendSnippetConfirm",
                    "alt": "extension.slackSendSnippetConfirm",
                    "group": "navigation"
                }
            ]
        }

コンテキストメニューからの実行と、ナビゲーターからの実行を追記しました。
完成したコードは下記です。

export function activate(context: vscode.ExtensionContext) {
    // ナビゲーター用のコマンド(テキスト)
    let slackSendTextConfirm = vscode.commands.registerCommand('extension.slackSendTextConfirm', () => {
        vscode.window.showInformationMessage("slackにtextを送信", {"title": "OK"}).then((value) => {
            if(!value) {
                return;
            }
            vscode.commands.executeCommand("extension.slackSendText")
        });
    });
    // ナビゲーター用のコマンド(スニペット)
    let slackSendSnippetConfirm = vscode.commands.registerCommand('extension.slackSendSnippetConfirm', () => {
        vscode.window.showInformationMessage("slackにsnipetを送信", {"title": "OK"}).then((value) => {
            if(!value) {
                return;
            }
            vscode.commands.executeCommand("extension.slackSendSnippet")
        });
    });
    let slackSendText = vscode.commands.registerCommand('extension.slackSendText', () => {
        const config = vscode.workspace.getConfiguration('slack');
        const url = config.general.postMessageUrl;
        const token = config.general.token;
        const channel = config.general.channel;
        let options = {
            url: url,
            form: {
                token: token,
                as_user: true,
                channel: channel,
                text: getText(vscode.window.activeTextEditor)
            }
        }
        sendMessage(options);
    });
    let slackSendSnippet = vscode.commands.registerCommand('extension.slackSendSnippet', () => {
        const config = vscode.workspace.getConfiguration('slack');
        const url = config.general.filesUploadUrl;
        const token = config.general.token;
        const channel = config.general.channel;
        let options = {
            url: url,
            form: {
                token: token,
                as_user: true,
                channels: channel,
                content: getText(vscode.window.activeTextEditor),
                filetype: "auto"
            }
        }
        sendMessage(options);
    });
    // エディタのテキストを取得する
    function getText(editor: any) {
        // editorの選択開始位置と終了位置が同じだったら未選択とし開いているドキュメントの内容を全て取得する。
        if(editor.selection.start.line == editor.selection.end.line && editor.selection.start.character == editor.selection.end.character) {
            return editor.document.getText();
        } else {
            return editor.document.getText(new vscode.Range(editor.selection.start, editor.selection.end));
        }
    }
    // 投稿する処理
    function sendMessage(options: any) {
        const request = require('request');
        request.post(options, (error: any, response: any, body: any) => {
            console.log(response);
        })
    }
    context.subscriptions.push(slackSendText);
    context.subscriptions.push(slackSendSnippet);
    context.subscriptions.push(slackSendTextConfirm);
    context.subscriptions.push(slackSendSnippetConfirm);
}

extensionをVS Codeに取り込む

前回と同じになりますが、今回作成したextensionを実際にデバックではないVS Codeで自分だけで使用する方法は、Windowsであれば最初にextensionを作成した場所(筆者の環境だとC:\Users\ユーザー名\拡張機能名)がextensionファイル一式になります。
これらをC:\Users\ユーザー名.vscode\extensionsにまるごとコピーしその後、VS Codeを再起動してください。起動後から使用出来ます。また別の方法ではVS CodeのMarketPlaceに公開し、インストールする方法もありますが、今回は実施はしませんので、もしもMarketPlaceに公開したい時はこちら(拡張機能の公開
)などを参考にしてみてください。

最後に

今回はVS Code+SlackのAPIを使って拡張機能を作成しました。
個人的にも今回の拡張機能は投稿時の利便性が上がったかなと思います。
SlackのAPIに限らず、VS Codeの拡張機能+様々なAPIの連携は本当に色々な事ができると思うので、是非1度試してみてください。

関連記事

運営会社:株式会社ラクーンホールディングス(c)2000 RACCOON HOLDINGS, Inc