Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -516,4 +516,11 @@ csharp_wrap_lines = false
# オブジェクト初期化子とコレクション初期化子のラップスタイルを指定する
csharp_wrap_object_and_collection_initializer_style = wrap_if_long
# プロパティパターンのラップスタイルを指定する
csharp_wrap_property_pattern = chop_if_long
csharp_wrap_property_pattern = chop_if_long
# フォルダ構造とnamespaceが違っても警告しない
dotnet_style_namespace_match_folder = false
# this を必須にする
dotnet_style_qualification_for_field = true:warning
dotnet_style_qualification_for_property = true:warning
dotnet_style_qualification_for_method = true:warning
dotnet_style_qualification_for_event = true:warning
10 changes: 3 additions & 7 deletions SharpChatwork/src/Attribute/EnumAliasAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,9 @@
namespace SharpChatwork;

[AttributeUsage(AttributeTargets.Field)]
internal sealed class EnumAliasAttribute : Attribute
internal sealed class EnumAliasAttribute(string aliasName) : Attribute
{
public EnumAliasAttribute(string aliasName)
{
this.AliasName = aliasName;
}
public string AliasName { get; set; }
public string aliasName { get; set; } = aliasName;
}

internal static class EnumAliasExtension
Expand All @@ -27,6 +23,6 @@ public static string ToAliasOrDefault(this Enum value)
if(i == null)
return Enum.GetName(value.GetType(), value);
// use alias
return i.AliasName;
return i.aliasName;
}
}
8 changes: 4 additions & 4 deletions SharpChatwork/src/Client/AccessToken/AccessTokenClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ namespace SharpChatwork.AccessToken;

public class AccessTokenClient(string accessToken, HttpMessageInvoker messageInvoker = null) : ChatworkClient
{
private readonly HttpMessageInvoker _messageInvoker = messageInvoker ?? new HttpClient();
private readonly HttpMessageInvoker MessageInvoker = messageInvoker ?? new HttpClient();
public override string clientName => nameof(AccessTokenClient);

private string accessToken { get; } = accessToken;
private string _accessToken { get; } = accessToken;

private HttpRequestMessage GenerateRequestMessage(Uri uri, HttpMethod method)
{
Expand All @@ -20,15 +20,15 @@ private HttpRequestMessage GenerateRequestMessage(Uri uri, HttpMethod method)
Method = method,
RequestUri = uri,
};
request.Headers.Add("X-ChatWorkToken", this.accessToken);
request.Headers.Add("X-ChatWorkToken", this._accessToken);
return request;
}

public override async ValueTask<ResponseWrapper> QueryAsync(Uri uri, HttpMethod method, HttpContent content, CancellationToken cancellation = default)
{
var requestMessage = this.GenerateRequestMessage(uri, method);
requestMessage.Content = content;
var client = this._messageInvoker;
var client = this.MessageInvoker;
var result = await client.SendAsync(requestMessage, cancellation);
var code = (int)result.StatusCode;
return new ResponseWrapper
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,10 @@

public class ChatworkClientException(ResponseWrapper response) : Exception
{
public readonly ResponseWrapper response = response;
public readonly ResponseWrapper Response = response;

Check warning on line 7 in SharpChatwork/src/Client/Exceptions/ChatworkClientException.cs

View workflow job for this annotation

GitHub Actions / build

Do not declare visible instance fields (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1051)

Check warning on line 7 in SharpChatwork/src/Client/Exceptions/ChatworkClientException.cs

View workflow job for this annotation

GitHub Actions / build

Do not declare visible instance fields (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1051)

Check warning on line 7 in SharpChatwork/src/Client/Exceptions/ChatworkClientException.cs

View workflow job for this annotation

GitHub Actions / build

Do not declare visible instance fields (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1051)

Check warning on line 7 in SharpChatwork/src/Client/Exceptions/ChatworkClientException.cs

View workflow job for this annotation

GitHub Actions / build

Do not declare visible instance fields (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1051)

public override string ToString()
{
return base.ToString() + $" --> ChatworkClientException: {this.Response.statusCode} {this.Response.content}";
}
}
2 changes: 2 additions & 0 deletions SharpChatwork/src/Client/IChatworkClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ namespace SharpChatwork;

public interface IChatworkClient
{
#pragma warning disable CA1716
IMeQuery me { get; }
#pragma warning restore CA1716
IRoomQuery room { get; }
IContactQuery contact { get; }
IIncomingRequestQuery incomingRequest { get; }
Expand Down
66 changes: 33 additions & 33 deletions SharpChatwork/src/Client/OAuth2/OAuth2Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,17 @@ public class OAuth2Client(string clientKey, string secretKey, HttpMessageInvoker
{
public override string clientName => nameof(OAuth2Client);

private readonly HttpMessageInvoker _messageInvoker = invoker ?? new HttpClient();
private string clientKey { get; } = clientKey;
private string secretKey { get; } = secretKey;
private string oauth2Code { get; set; } = string.Empty;
private string accessToken { get; set; } = string.Empty;
private string refleshToken { get; set; } = string.Empty;
private long tokenExpired { get; set; } = 0;
private readonly HttpMessageInvoker MessageInvoker = invoker ?? new HttpClient();
private string _clientKey { get; } = clientKey;
private string _secretKey { get; } = secretKey;
private string _oauth2Code { get; set; } = string.Empty;
private string _accessToken { get; set; } = string.Empty;
private string _refreshToken { get; set; } = string.Empty;
private long _tokenExpired { get; set; } = 0;

private string scope { get; set; } = string.Empty;
private string redirectUri { get; set; } = string.Empty;
private DateTime tokenQueryTime { get; set; } = DateTime.Now;
private string _scope { get; set; } = string.Empty;
private string _redirectUri { get; set; } = string.Empty;
private DateTime _tokenQueryTime { get; set; } = DateTime.Now;

private HttpRequestMessage GenerateRequestMessage(Uri uri, HttpMethod method)
{
Expand All @@ -36,15 +36,15 @@ private HttpRequestMessage GenerateRequestMessage(Uri uri, HttpMethod method)
Method = method,
RequestUri = uri,
};
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", this.accessToken);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", this._accessToken);
return request;
}

public override async ValueTask<ResponseWrapper> QueryAsync(Uri uri, HttpMethod method, HttpContent content, CancellationToken cancellation = default)
{
var requestMessage = this.GenerateRequestMessage(uri, method);
requestMessage.Content = content;
var client = this._messageInvoker;
var client = this.MessageInvoker;
var result = await client.SendAsync(requestMessage, cancellation);
var code = (int)result.StatusCode;

Expand All @@ -58,41 +58,41 @@ public override async ValueTask<ResponseWrapper> QueryAsync(Uri uri, HttpMethod

public OAuth2ConcentQueryResult Authorization(OAuth2ConcentQuery query, string codeVerifer = "")
{
query.client_id = this.clientKey;
this.scope = query.scope;
this.redirectUri = query.redirect_uri;
query.client_id = this._clientKey;
this._scope = query.scope;
this._redirectUri = query.redirect_uri;
// TODO Only windows
var concentUrlArg = EndPoints.Oauth2.OriginalString + $"{UrlArgEncoder.ToURLArg(query)}";
var contentUrlArg = EndPoints.Oauth2.OriginalString + $"{UrlArgEncoder.ToURLArg(query)}";
Console.WriteLine("Please input code of redirect url code=");
Process.Start(
new ProcessStartInfo("cmd", $"/c start {concentUrlArg}")
new ProcessStartInfo("cmd", $"/c start {contentUrlArg}")
{
CreateNoWindow = true,
}
);
this.oauth2Code = Console.ReadLine();
this._oauth2Code = Console.ReadLine();

if(string.IsNullOrEmpty(this.oauth2Code))
if(string.IsNullOrEmpty(this._oauth2Code))
{
return new OAuth2ConcentQueryResult
{
error = "oauth_code_error",
//error_description = "inputed oauth_code is null or empty",
};
}
query.client_id = this.clientKey;
query.client_id = this._clientKey;
return new OAuth2ConcentQueryResult
{
code = this.oauth2Code,
code = this._oauth2Code,
};
}

public async Task<OAuth2TokenQueryResult> UpdateToken(OAuth2TokenQuery.GrantType grantType = OAuth2TokenQuery.GrantType.RefreshToken, string codeVerifer = "", CancellationToken cancellation = default)
public async Task<OAuth2TokenQueryResult> UpdateTokenAsync(OAuth2TokenQuery.GrantType grantType = OAuth2TokenQuery.GrantType.RefreshToken, string codeVerifer = "", CancellationToken cancellation = default)
{
var tokenQuery = new OAuth2TokenQuery(grantType)
{
scope = this.scope,
redirect_uri = this.redirectUri,
scope = this._scope,
redirect_uri = this._redirectUri,
};
var request = new HttpRequestMessage
{
Expand All @@ -101,26 +101,26 @@ public async Task<OAuth2TokenQueryResult> UpdateToken(OAuth2TokenQuery.GrantType
};

if(grantType == OAuth2TokenQuery.GrantType.AuthroizationCode)
tokenQuery.code = this.oauth2Code;
tokenQuery.code = this._oauth2Code;
else if(grantType == OAuth2TokenQuery.GrantType.RefreshToken)
tokenQuery.refresh_token = this.refleshToken;
tokenQuery.refresh_token = this._refreshToken;

request.Headers.Authorization = new AuthenticationHeaderValue(
"Basic",
Convert.ToBase64String(Encoding.ASCII.GetBytes($"{this.clientKey}:{this.secretKey}"))
Convert.ToBase64String(Encoding.ASCII.GetBytes($"{this._clientKey}:{this._secretKey}"))
);
request.Content = new FormUrlEncodedContent(UrlArgEncoder.ToDictionary(tokenQuery));

var client = this._messageInvoker;
var client = this.MessageInvoker;
var response = await client.SendAsync(request, cancellation);
var stream = await response.Content.ReadAsStreamAsync();

using var reader = new StreamReader(stream);
var result = JsonSerializer.Deserialize<OAuth2TokenQueryResult>(reader.ReadToEnd());
this.tokenExpired = result.expires_in;
this.tokenQueryTime = DateTime.Now;
this.refleshToken = result.refresh_token;
this.accessToken = result.access_token;
var result = JsonSerializer.Deserialize<OAuth2TokenQueryResult>(await reader.ReadToEndAsync());
this._tokenExpired = result.expires_in;
this._tokenQueryTime = DateTime.Now;
this._refreshToken = result.refresh_token;
this._accessToken = result.access_token;
return result;
}
}
2 changes: 2 additions & 0 deletions SharpChatwork/src/Client/OAuth2/OAuth2ConcentQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ namespace SharpChatwork.OAuth2;

public class OAuth2ConcentQuery
{
#pragma warning disable CA1822
public string response_type => "code";
#pragma warning restore CA1822
public string client_id { get; set; } = string.Empty;
public string redirect_uri { get; set; } = string.Empty;
public string scope { get; set; } = string.Empty;
Expand Down
9 changes: 2 additions & 7 deletions SharpChatwork/src/Client/OAuth2/OAuth2TokenQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace SharpChatwork.OAuth2;

public class OAuth2TokenQuery
public class OAuth2TokenQuery(OAuth2TokenQuery.GrantType type)
{
public enum GrantType
{
Expand All @@ -12,7 +12,7 @@ public enum GrantType
RefreshToken,
}

public string grant_type { get; set; } = string.Empty;
public string grant_type { get; set; } = type.ToAliasOrDefault();
public string code { get; set; } = string.Empty;
public string redirect_uri { get; set; } = string.Empty;
public string code_verifier { get; set; } = string.Empty;
Expand All @@ -25,9 +25,4 @@ public ScopeType scopeType
get => this._scopeType;
set { this._scopeType = value; this.scope = this._scopeType.ToUrlArg(); }
}

public OAuth2TokenQuery(GrantType type)
{
this.grant_type = type.ToAliasOrDefault();
}
}
28 changes: 15 additions & 13 deletions SharpChatwork/src/Client/OAuth2/Scope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
namespace SharpChatwork.OAuth2;

/// <summary>
/// API access scopes <para/>
/// implement from here http://developer.chatwork.com/ja/oauth.html#secAppendix
/// API access scopes
/// <para />
/// implement from here http://developer.chatwork.com/ja/oauth.html#secAppendix
/// </summary>
public enum ScopeType : long
{
Expand All @@ -17,7 +18,7 @@ public enum ScopeType : long

[Description("自分のアカウントに紐づく情報の取得")]
[EnumAlias("users.all:read")]
UsersAllR = UsersProfileMeR | UsersTasksMeR | UsersStatusMeR,
UsersAllR = ScopeType.UsersProfileMeR | ScopeType.UsersTasksMeR | ScopeType.UsersStatusMeR,

[Description("自分のプロフィール情報の取得")]
[EnumAlias("users.profile.me:read")]
Expand All @@ -33,7 +34,7 @@ public enum ScopeType : long

[Description("チャットルームに紐づくメッセージ・タスク・ファイル・概要・メンバー情報の操作/取得")]
[EnumAlias("rooms.all:read_write")]
RoomsAllRW = RoomsAllR | RoomsAllW,
RoomsAllRW = ScopeType.RoomsAllR | ScopeType.RoomsAllW,

[Description("チャットルームに紐づくメッセージ・タスク・ファイル・概要・メンバー情報の取得")]
[EnumAlias("rooms.all:read")]
Expand Down Expand Up @@ -89,7 +90,7 @@ public enum ScopeType : long

[Description("自分のコンタクト、及びコンタクト承認依頼情報の取得/操作")]
[EnumAlias("contacts.all:read_write")]
ContactsAllRW = ContactsAllR | ContactsAllW,
ContactsAllRW = ScopeType.ContactsAllR | ScopeType.ContactsAllW,

[Description("自分のコンタクト、及びコンタクト承認依頼情報の取得")]
[EnumAlias("contacts.all:read")]
Expand All @@ -106,14 +107,19 @@ public static string ToUrlArg(this ScopeType type)
{
// extract name value pair
var enumValues = Enum.GetValues(typeof(ScopeType)).OfType<ScopeType>();
var enumNames = enumValues.Select(m => FindAttribute<EnumAliasAttribute>(m));
var enumNames = enumValues.Select(ScopeTypeExtension.FindAttribute<EnumAliasAttribute>);
var input = (long)type;
var enumNameValues = enumNames
.Zip(enumValues, (m, n) => new { m.AliasName, Value = n })
.Zip(
enumValues, (m, n) => new
{
AliasName = m.aliasName,
Value = n,
}
)
.Where(m => ((long)m.Value & input) != 0)
.Reverse();

List<Tuple<string, long>> resultScopes = new List<Tuple<string, long>>();
var resultScopes = new List<Tuple<string, long>>();

// escape _all child
foreach(var item in enumNameValues)
Expand All @@ -134,12 +140,8 @@ private static AttributeT FindAttribute<AttributeT>(ScopeType type) where Attrib
var attributes = fieldInfo
.GetCustomAttributes(typeof(AttributeT), false)
.Cast<AttributeT>();

if(attributes == null)
return null;
if(!attributes.Any())
return null;

return attributes.First();
}
}
6 changes: 1 addition & 5 deletions SharpChatwork/src/Client/Query/RoomFileQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,8 @@

namespace SharpChatwork.Query;

internal sealed class RoomFileQuery : ClientQuery, IRoomFileQuery
internal sealed class RoomFileQuery(IChatworkClient client) : ClientQuery(client), IRoomFileQuery
{
public RoomFileQuery(IChatworkClient client) : base(client)
{
}

public async ValueTask<IEnumerable<UserFile>> GetAllAsync(long roomId, long accountId, CancellationToken token = default)
{
var uri = $"{EndPoints.RoomFiles(roomId)}?account_id={accountId}";
Expand Down
5 changes: 3 additions & 2 deletions SharpChatwork/src/Client/Query/RoomInviteQuery.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Globalization;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
Expand All @@ -14,7 +15,7 @@ public async ValueTask<InviteLink> CreateAsync(long roomId, string uniqueName, s
{
{"code", uniqueName},
{"description", description},
{"need_acceptance", UrlArgEncoder.BoolToInt(requireAcceptance).ToString()},
{"need_acceptance", UrlArgEncoder.BoolToInt(requireAcceptance).ToString(CultureInfo.InvariantCulture)},
};
return await this.chatworkClient.QueryAsync<InviteLink>(EndPoints.RoomTasks(roomId), HttpMethod.Post, data, token);
}
Expand All @@ -35,7 +36,7 @@ public async ValueTask<InviteLink> UpdateAsync(long roomId, string uniqueName, s
{
{"code", uniqueName},
{"description", description},
{"need_acceptance", UrlArgEncoder.BoolToInt(requireAcceptance).ToString()},
{"need_acceptance", UrlArgEncoder.BoolToInt(requireAcceptance).ToString(CultureInfo.InvariantCulture)},
};
return await this.chatworkClient.QueryAsync<InviteLink>(EndPoints.RoomTasks(roomId), HttpMethod.Put, data, token);
}
Expand Down
3 changes: 2 additions & 1 deletion SharpChatwork/src/Client/Query/RoomMessageQuery.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
Expand Down Expand Up @@ -38,7 +39,7 @@ public async ValueTask<MessageId> SendAsync(long roomId, string message, bool is
var data = new Dictionary<string, string>
{
{"body", message},
{"self_unread", UrlArgEncoder.BoolToInt(isSelfUnread).ToString()},
{"self_unread", UrlArgEncoder.BoolToInt(isSelfUnread).ToString(CultureInfo.InvariantCulture)},
};
return await this.chatworkClient.QueryAsync<MessageId>(EndPoints.RoomMessages(roomId), HttpMethod.Post, data, token);
}
Expand Down
Loading
Loading