using System;
using System.IO;
using System.Net;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using System.Text.Json.Serialization;
using ChatworkBulkSender.Daos;
using ChatworkBulkSender.Dtos;
using System.Linq;
namespace ChatworkBulkSender.Services.Chatwork
{
public class ChatworkService
{
private const string BaseUrl = "https://api.chatwork.com/v2";
private readonly string _apiToken;
public ChatworkService()
{
// APIトークン取得
var senderMasterDto = new SenderMasterDao().GetAll().FirstOrDefault();
_apiToken = senderMasterDto.SenderApiToken;
}
/// <summary>
/// チャット送信処理
/// </summary>
/// <param name="roomId"></param>
/// <param name="message"></param>
/// <returns></returns>
public async Task<SendResult> SendMessageAsync(string roomId, string message)
{
var result = new SendResult();
var url = $"{BaseUrl}/rooms/{roomId}/messages";
var request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "POST";
request.Headers.Add("X-ChatWorkToken", _apiToken);
// フォーム URL エンコード
var postData = $"body={Uri.EscapeDataString(message)}";
var dataBytes = Encoding.UTF8.GetBytes(postData);
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = dataBytes.Length;
try
{
// リクエスト送信
using (var reqStream = await request.GetRequestStreamAsync().ConfigureAwait(false))
{
await reqStream.WriteAsync(dataBytes, 0, dataBytes.Length).ConfigureAwait(false);
}
// レスポンス読み取り
using (var response = (HttpWebResponse)await request.GetResponseAsync().ConfigureAwait(false))
using (var sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
{
var json = await sr.ReadToEndAsync().ConfigureAwait(false);
var msgResp = JsonSerializer.Deserialize<MessageResponse>(json);
if (msgResp == null || string.IsNullOrEmpty(msgResp.MessageId))
{
result.Success = Utils.Constants.SEND_RESULT.FAILURE;
result.SystemErrorMessage = $"レスポンスの解析に失敗しました: {json}";
return result;
}
result.Success = Utils.Constants.SEND_RESULT.SUCCESS;
result.MessageId = msgResp.MessageId;
}
}
catch (WebException wex)
{
string errorBody = "";
if (wex.Response != null)
{
using (var er = wex.Response.GetResponseStream())
using (var sr = new StreamReader(er, Encoding.UTF8))
{
errorBody = await sr.ReadToEndAsync().ConfigureAwait(false);
}
}
result.Success = Utils.Constants.SEND_RESULT.FAILURE;
result.SystemErrorMessage = $"[WebException] {wex.Message} ResponseBody={errorBody}";
}
catch (Exception ex)
{
result.Success = Utils.Constants.SEND_RESULT.FAILURE;
result.SystemErrorMessage = ex.Message;
}
finally
{
result.ApiToken = _apiToken;
result.RoomId = roomId;
result.SendCompletedDt = DateTime.Now;
result.SendAttempted = Utils.Constants.SEND_ATTEMPTED.ATTEMPTED;
result.SetErrorMessageForDisp();
}
return result;
}
/// <summary>
/// ファイルアップロード+チャット送信処理
/// </summary>
/// <param name="roomId"></param>
/// <param name="filePath"></param>
/// <param name="message"></param>
/// <returns></returns>
public async Task<SendResult> UploadFileAsync(string roomId, string filePath, string message)
{
// Return用の送信結果クラス
var result = new SendResult();
// API設定
var url = $"{BaseUrl}/rooms/{roomId}/files";
var boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
var request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "POST";
request.Headers.Add("X-ChatWorkToken", _apiToken);
request.ContentType = $"multipart/form-data; boundary={boundary}";
try
{
// 送信ファイル読み込み
byte[] fileBytes = File.ReadAllBytes(filePath);
var fileName = Path.GetFileName(filePath);
// マルチパート組み立て
using (var ms = new MemoryStream())
{
var sb = new StringBuilder();
// --- part: file ---
sb.Append("--").Append(boundary).Append("\r\n");
sb.Append("Content-Disposition: form-data; name=\"file\"; filename=\"").Append(fileName).Append("\"\r\n");
sb.Append("Content-Type: application/pdf\r\n\r\n");
WriteString(ms, sb.ToString());
ms.Write(fileBytes, 0, fileBytes.Length);
WriteString(ms, "\r\n");
// --- part: message (任意) ---
if (!string.IsNullOrEmpty(message))
{
sb.Clear();
sb.Append("--").Append(boundary).Append("\r\n");
sb.Append("Content-Disposition: form-data; name=\"message\"\r\n\r\n");
sb.Append(message).Append("\r\n");
WriteString(ms, sb.ToString());
}
// --- end ---
WriteString(ms, "--" + boundary + "--\r\n");
request.ContentLength = ms.Length;
using (var reqStream = await request.GetRequestStreamAsync().ConfigureAwait(false))
{
ms.Position = 0;
await ms.CopyToAsync(reqStream).ConfigureAwait(false);
}
}
// レスポンス取得
using (var response = (HttpWebResponse)await request.GetResponseAsync().ConfigureAwait(false))
using (var sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
{
var json = await sr.ReadToEndAsync().ConfigureAwait(false);
var uploadResp = JsonSerializer.Deserialize<UploadResponse>(json);
// 想定外のレスポンスの場合、送信処理が失敗したとみなす
if (uploadResp == null || uploadResp.FileId <= 0)
{
result.Success = Utils.Constants.SEND_RESULT.FAILURE;
result.SystemErrorMessage = $"送信処理結果の解析に失敗しました。{json}";
return result;
}
// 送信処理成功
result.Success = Utils.Constants.SEND_RESULT.SUCCESS;
result.FileId = uploadResp.FileId.ToString();
}
}
catch (WebException wex)
{
// HTTPエラー(4xx/5xxなど)の場合にもレスポンスボディを読んで返す
string errorBody = "";
if (wex.Response != null)
{
using (var er = wex.Response.GetResponseStream())
using (var sr = new StreamReader(er))
{
errorBody = await sr.ReadToEndAsync().ConfigureAwait(false);
}
}
result.Success = Utils.Constants.SEND_RESULT.FAILURE;
result.SystemErrorMessage = $"[WebException] {wex.Message} ResponseBody={errorBody}";
}
catch (Exception ex)
{
// その他例外
result.Success = Utils.Constants.SEND_RESULT.FAILURE;
result.SystemErrorMessage = ex.Message;
}
finally
{
result.ApiToken = _apiToken;
result.RoomId = roomId;
result.SendCompletedDt = DateTime.Now;
result.SendAttempted = Utils.Constants.SEND_ATTEMPTED.ATTEMPTED;
// ユーザー向けメッセージをセット
result.SetErrorMessageForDisp();
}
return result;
}
/// <summary>
/// RoomIdが存在するか返す。
/// </summary>
/// <param name="roomId">確認したいルームID(数値文字列)</param>
/// <returns>存在すれば true、取得エラーや未発見なら false</returns>
public async Task<bool> IsRoomExistsAsync(string roomId)
{
// まず roomId が数値として妥当かをチェック
if (!long.TryParse(roomId, out var targetId))
return false;
var url = $"{BaseUrl}/rooms";
var request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "GET";
request.Headers.Add("X-ChatWorkToken", _apiToken);
try
{
using (var response = (HttpWebResponse)await request.GetResponseAsync().ConfigureAwait(false))
using (var sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
{
var json = await sr.ReadToEndAsync().ConfigureAwait(false);
using (var doc = JsonDocument.Parse(json))
{
// rooms API のレスポンスは配列
foreach (var elem in doc.RootElement.EnumerateArray())
{
// 各要素に "room_id" プロパティがあるか
if (elem.TryGetProperty("room_id", out var idProp)
&& idProp.ValueKind == JsonValueKind.Number
&& idProp.GetInt64() == targetId)
{
return true;
}
}
}
}
}
catch (WebException)
{
// 何らかのHTTPエラー(認証失敗など)は「存在しない」とみなす
return false;
}
catch
{
return false;
}
// 見つからなければ false
return false;
}
/// <summary>
/// RoomId に指定した AccountId がメンバーとして存在するか返す。
/// </summary>
/// <param name="roomId">チャットワークのルームID</param>
/// <param name="accountId">確認したいアカウントID(文字列)</param>
/// <returns>メンバーに存在すれば true、そうでなければ false</returns>
public async Task<bool> IsAccountInRoomAsync(string roomId, string accountId)
{
var url = $"{BaseUrl}/rooms/{roomId}/members";
var request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "GET";
request.Headers.Add("X-ChatWorkToken", _apiToken);
try
{
using (var response = (HttpWebResponse)await request.GetResponseAsync().ConfigureAwait(false))
using (var sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
{
var json = await sr.ReadToEndAsync().ConfigureAwait(false);
// JSON をパースして account_id フィールドをチェック
using (var doc = JsonDocument.Parse(json))
{
foreach (var elem in doc.RootElement.EnumerateArray())
{
if (elem.TryGetProperty("account_id", out var idProp))
{
// account_id は数値なので ToString() して比較
if (idProp.GetRawText().Trim('"') == accountId)
return true;
}
}
}
}
}
catch (WebException)
{
// 404 や認証エラーなども含め「存在しない」扱いにしたい場合は false を返す
return false;
}
catch
{
return false;
}
return false;
}
private static void WriteString(Stream s, string str)
{
var buf = Encoding.UTF8.GetBytes(str);
s.Write(buf, 0, buf.Length);
}
}
/// <summary>
/// ChatworkAPIでメッセージ送信した場合のレスポンス
/// </summary>
public class MessageResponse
{
[JsonPropertyName("message_id")]
public string MessageId { get; set; }
}
/// <summary>
/// ChatworkAPIでファイルアップロードした場合のレスポンス
/// Chatworkのレスポンス例:
/// { "file_id":12345, "message_id":"1111222333" }
/// </summary>
public class UploadResponse
{
[JsonPropertyName("file_id")]
public long FileId { get; set; }
}
}