これまでChatworkというチャットアプリのAPIを使っていろいろな自動化をしていたのですが、旧に会社の方針でChatwork廃止、Slackにするぞとなりました。
個人的には最初からSlackの方が高機能で良さそうだと思っていた~というのは愚痴ですが詰まるところ、Slackにこれまで作ってきたものを移植しないといけなくなったわけです。
寝耳に水。
Slackメッセージ送信は超簡単、ファイルのアップロードは?
メッセージの取得だけに限れば、curlでWebhook URLを叩けば一発でメッセージを送信できます。あら簡単。
curl -X POST -H "Content-type: application/json" --data "{\"text\":\"test Message"\"}" https://hooks.slack.com/services/T0XXXXXX/B0XXXXX/XXXXXX
ところが、Webhook URLで利用出来るのはメッセージの送信のみで、メッセージの受信はもちろん、ファイルのアップロードなどもできないのです。
ファイルのアップロードについて
これまであったSlackファイルアップロードAPI(files.upload)が2025年3月に廃止されるそうで、現在、既に新規作成したアプリは使用不可になっています。
これが世の中のサンプルがなぜか動かない原因と分かり、それまで難儀してました。
結論から言いうと、代わりに下記を使いましょう**となってます。SharePointとかも似た方式に移行してますね。
- sequenced Web API
- files.getUploadURLExternal
- files.completeUploadExternal
ちなみに、公式?とおぼしきSlackAPI というライブラリがあり、それを使えば良いじゃんと思いましたが、1年前に更新が止まってしまっていて、上記のAPIには対応していませんでした。
さぁ四の五の言わず、アップロード関数サンプルを(C#)
using System.Diagnostics;
using System.Net.Http.Headers;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
private String Token = "YoutBotToken";
private String SlackApiEntryPoint = "https://slack.com/api";
public async void UploadFileAsync(String channelId, String filePath)
{
var fileData = await GetUploadURLExternal(filePath);
await this.UploadFile(fileData.url, filePath);
await this.CompleteUploadExternal(fileData.fileId, filePath, channelId);
}
private async Task<(string url, string fileId)> GetUploadURLExternal(string filePath)
{
using (var client = new HttpClient()) {
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Token);
var baseUrl = $"{SlackApiEntryPoint}/files.getUploadURLExternal";
var response = await client.GetAsync($"{baseUrl}?filename={Path.GetFileName(filePath)}&length={new FileInfo(filePath).Length}");
response.EnsureSuccessStatusCode();
var jsonResponse = JObject.Parse(await response.Content.ReadAsStringAsync());
if (!jsonResponse["ok"].Value<bool>()) {
throw new Exception($"Error : files.getUploadURLExternal, {jsonResponse["error"]}.");
}
return (jsonResponse["upload_url"].ToString(), jsonResponse["file_id"].ToString());
}
}
private async Task UploadFile(String url, String filePath)
{
using (var client = new HttpClient())
using (var form = new MultipartFormDataContent()) {
using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read)) {
var streamContent = new StreamContent(fileStream);
streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
form.Add(streamContent, "file", Path.GetFileName(filePath));
var response = await client.PostAsync(url, form);
if (response.IsSuccessStatusCode == false) {
throw new Exception($"Error : UploadFile failed, status {response.StatusCode}");
}
}
}
}
private async Task<JObject> CompleteUploadExternal(string fileId, string filePath, string channelId)
{
using (var client = new HttpClient()) {
var json = new JObject {
{ "channel_id", channelId },
{ "files", new JArray { new JObject{
{ "id", fileId },
{ "title", Path.GetFileName(filePath) }
}
} }
};
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Token);
var content = new StringContent(json.ToString());
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var response = await client.PostAsync($"{SlackApiEntryPoint}/files.completeUploadExternal", content);
response.EnsureSuccessStatusCode();
var jsonResponse = JObject.Parse(await response.Content.ReadAsStringAsync());
if (!jsonResponse["ok"].Value<bool>()) {
throw new Exception($"Error : files.completeUploadExternal, {jsonResponse["error"]}.");
}
return jsonResponse;
}
}
必要な情報
必要なものは、「アクセス許可諸々有効にしたトークン Token(この場合はBotToken)」「投稿先のチャンネルID channelId」「アップロードしたいファイル filePath」を用意して非同期関数のUploadFileAsyncを叩けばファイルがアップロードできます。
ここでは説明省略しますが、https://app.slack.comであらかじめアプリケーションを作成しトークンを発行、Permissionの許可設定とInstallという作業をしてアクセス許可を行っておく必要がある点に注意です。投稿先のチャンネルに作成したアプリケーションを追加するのも忘れずに(これに相当ハマった)。
処理構成
処理構成を簡単に説明するとUploadFileAsync関数は下記3つで構成されており、 順に呼び出す必要があります。
- GetUploadURLExternal() ... GETでファイル情報を渡してURLとIDを取得
- UploadFile() ... POSTで取得URIへファイルアップロード
- CompleteUploadExternal() ... POST/JSONでアップロード完了を通知
整理したら簡単ですね。
もしうまくいかない場合は、レスポンスのerror項目を見るとヒントになります。
さて、他の細々としたAPIも理解して対応していかねばといった次第……。未読のメッセージが(情報なしに)簡単に取得できなさそうで、ぐぬぬとなっております。
api.slack.com