Initial commit

This commit is contained in:
Chris Chen
2022-09-08 08:04:32 -07:00
commit 184db15773
4604 changed files with 503905 additions and 0 deletions
+54
View File
@@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace LineMessaging
{
public partial class LineMessagingClient
{
private const string GroupMemberApiPath = "/v2/bot/group/{0}/member/{1}";
private const string GroupMembersApiPath = "/v2/bot/group/{0}/members/ids";
private const string LeaveGroupApiPath = "/v2/bot/group/{0}/leave";
public async Task<LineProfile> GetGroupMember(string groupId, string userId)
{
if (string.IsNullOrEmpty(groupId))
{
throw new ArgumentException($"{nameof(groupId)} is null or empty.");
}
if (string.IsNullOrEmpty(userId))
{
throw new ArgumentException($"{nameof(userId)} is null or empty.");
}
return await Get<LineProfile>(string.Format(GroupMemberApiPath, groupId, userId));
}
public async Task<LineMembers> GetGroupMembers(string groupId, string start = null)
{
if (string.IsNullOrEmpty(groupId))
{
throw new ArgumentException($"{nameof(groupId)} is null or empty.");
}
var query = new Dictionary<string, object>();
if (start != null)
{
query["start"] = start;
}
return await Get<LineMembers>(string.Format(GroupMembersApiPath, groupId), query);
}
public async Task LeaveGroup(string groupId)
{
if (string.IsNullOrEmpty(groupId))
{
throw new ArgumentException($"{nameof(groupId)} is null or empty.");
}
await Post(string.Format(LeaveGroupApiPath, groupId));
}
}
}
@@ -0,0 +1,20 @@
using System;
using System.Threading.Tasks;
namespace LineMessaging
{
public partial class LineMessagingClient
{
private const string MessageContentApiPath = "/v2/bot/message/{0}/content";
public Task<byte[]> GetMessageContent(string messageId)
{
if (string.IsNullOrEmpty(messageId))
{
throw new ArgumentException($"{nameof(messageId)} is null or empty.");
}
return GetAsByteArray(string.Format(MessageContentApiPath, messageId));
}
}
}
+255
View File
@@ -0,0 +1,255 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace LineMessaging
{
public partial class LineMessagingClient
{
private const string LineMessagePushApiPath = "/v2/bot/message/push";
private const string LineMessageMulticastApiPath = "/v2/bot/message/multicast";
public async Task PushMessage(LinePushMessage pushMessage)
{
if (pushMessage == null)
{
throw new ArgumentNullException(nameof(pushMessage));
}
if (string.IsNullOrEmpty(pushMessage.To))
{
throw new ArgumentException($"{nameof(pushMessage.To)} is null or empty.");
}
var messageCount = pushMessage.Messages.Count();
if (pushMessage.Messages == null || messageCount == 0 || messageCount > 5)
{
throw new ArgumentException($"{nameof(pushMessage.Messages)} is required and max length is 5.");
}
await Post(LineMessagePushApiPath, pushMessage);
}
public async Task PushMessage(string to, ILineMessage message)
{
if (to == null)
{
throw new ArgumentNullException(nameof(to));
}
if (message == null)
{
throw new ArgumentNullException(nameof(message));
}
await Post(LineMessagePushApiPath, new LinePushMessage
{
To = to,
Messages = new List<ILineMessage> { message }
});
}
public async Task PushMessage(string to, IEnumerable<ILineMessage> messages)
{
if (to == null)
{
throw new ArgumentNullException(nameof(to));
}
if (messages == null)
{
throw new ArgumentNullException(nameof(messages));
}
var messageCount = messages.Count();
if (messageCount == 0 || messageCount > 5)
{
throw new ArgumentException($"{nameof(messages)} is required and max length is 5.");
}
await Post(LineMessagePushApiPath, new LinePushMessage
{
To = to,
Messages = messages
});
}
public async Task PushMessage(string to, string message)
{
if (to == null)
{
throw new ArgumentNullException(nameof(to));
}
if (string.IsNullOrEmpty(message))
{
throw new ArgumentException($"{nameof(message)} is null or empty.");
}
await Post(LineMessagePushApiPath, new LinePushMessage
{
To = to,
Messages = new List<ILineMessage> { new LineTextMessage { Text = message } }
});
}
public async Task PushMessage(string to, params string[] messages)
{
if (to == null)
{
throw new ArgumentNullException(nameof(to));
}
if (messages == null)
{
throw new ArgumentNullException(nameof(messages));
}
if (messages.Length == 0 || messages.Length > 5)
{
throw new ArgumentException($"{nameof(messages)} is required and max length is 5.");
}
await Post(LineMessagePushApiPath, new LinePushMessage
{
To = to,
Messages = messages.Select(x => (ILineMessage)new LineTextMessage { Text = x }).ToList()
});
}
public async Task MulticastMessage(LineMulticastMessage multicastMessage)
{
if (multicastMessage == null)
{
throw new ArgumentNullException(nameof(multicastMessage));
}
var toCount = multicastMessage.To.Count();
if (multicastMessage.To == null || toCount == 0 || toCount > 150)
{
throw new ArgumentException($"{nameof(multicastMessage.To)} is required and max length is 150.");
}
var messageCount = multicastMessage.Messages.Count();
if (multicastMessage.Messages == null || messageCount == 0 || messageCount > 5)
{
throw new ArgumentException($"{nameof(multicastMessage.Messages)} is required and max length is 5.");
}
await Post(LineMessageMulticastApiPath, multicastMessage);
}
public async Task MulticastMessage(IEnumerable<string> to, ILineMessage message)
{
if (to == null)
{
throw new ArgumentNullException(nameof(to));
}
if (message == null)
{
throw new ArgumentNullException(nameof(message));
}
var toCount = to.Count();
if (toCount == 0 || toCount > 150)
{
throw new ArgumentException($"{nameof(to)} is required and max length is 150.");
}
await Post(LineMessageMulticastApiPath, new LineMulticastMessage
{
To = to,
Messages = new List<ILineMessage> { message }
});
}
public async Task MulticastMessage(IEnumerable<string> to, IEnumerable<ILineMessage> messages)
{
if (to == null)
{
throw new ArgumentNullException(nameof(to));
}
if (messages == null)
{
throw new ArgumentNullException(nameof(messages));
}
var toCount = to.Count();
if (toCount == 0 || toCount > 150)
{
throw new ArgumentException($"{nameof(to)} is required and max length is 150.");
}
var messageCount = messages.Count();
if (messageCount == 0 || messageCount > 5)
{
throw new ArgumentException($"{nameof(messages)} is required and max length is 5.");
}
await Post(LineMessageMulticastApiPath, new LineMulticastMessage
{
To = to,
Messages = messages
});
}
public async Task MulticastMessage(IEnumerable<string> to, string message)
{
if (to == null)
{
throw new ArgumentNullException(nameof(to));
}
if (string.IsNullOrEmpty(message))
{
throw new ArgumentException($"{nameof(message)} is null or empty.");
}
var toCount = to.Count();
if (toCount == 0 || toCount > 150)
{
throw new ArgumentException($"{nameof(to)} is required and max length is 150.");
}
await Post(LineMessageMulticastApiPath, new LineMulticastMessage
{
To = to,
Messages = new List<ILineMessage> { new LineTextMessage { Text = message } }
});
}
public async Task MulticastMessage(IEnumerable<string> to, params string[] messages)
{
if (to == null)
{
throw new ArgumentNullException(nameof(to));
}
if (messages == null)
{
throw new ArgumentNullException(nameof(messages));
}
var toCount = to.Count();
if (toCount == 0 || toCount > 150)
{
throw new ArgumentException($"{nameof(to)} is required and max length is 150.");
}
var messageCount = messages.Count();
if (messageCount == 0 || messageCount > 5)
{
throw new ArgumentException($"{nameof(messages)} is required and max length is 5.");
}
await Post(LineMessageMulticastApiPath, new LineMulticastMessage
{
To = to,
Messages = messages.Select(x => (ILineMessage)new LineTextMessage { Text = x }).ToList()
});
}
}
}
+101
View File
@@ -0,0 +1,101 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace LineMessaging
{
public partial class LineMessagingClient
{
private const string MessageReplyApiPath = "/v2/bot/message/reply";
public async Task ReplyMessage(LineReplyMessage replyMessage)
{
if (replyMessage == null)
{
throw new ArgumentNullException(nameof(replyMessage));
}
if (string.IsNullOrEmpty(replyMessage.ReplyToken))
{
throw new ArgumentException($"{nameof(replyMessage.ReplyToken)} is null or empty.");
}
var messageCount = replyMessage.Messages.Count();
if (replyMessage.Messages == null || messageCount == 0 || messageCount > 5)
{
throw new ArgumentException($"{nameof(replyMessage.Messages)} is required and max length is 5.");
}
await Post(MessageReplyApiPath, replyMessage);
}
public async Task ReplyMessage(string replyToken, IEnumerable<ILineMessage> messages)
{
if (string.IsNullOrEmpty(replyToken))
{
throw new ArgumentException($"{nameof(replyToken)} is null or empty.");
}
if (messages == null)
{
throw new ArgumentNullException(nameof(messages));
}
var messageCount = messages.Count();
if (messageCount == 0 || messageCount > 5)
{
throw new ArgumentException($"{nameof(messages)} is required and max length is 5.");
}
await Post(MessageReplyApiPath, new LineReplyMessage
{
ReplyToken = replyToken,
Messages = messages
});
}
public async Task ReplyMessage(string replyToken, string message)
{
if (string.IsNullOrEmpty(replyToken))
{
throw new ArgumentException($"{nameof(replyToken)} is null or empty.");
}
if (string.IsNullOrEmpty(message))
{
throw new ArgumentException($"{nameof(message)} is null or empty.");
}
await Post(MessageReplyApiPath, new LineReplyMessage
{
ReplyToken = replyToken,
Messages = new List<ILineMessage> { new LineTextMessage { Text = message } }
});
}
public async Task ReplyMessage(string replyToken, params string[] messages)
{
if (string.IsNullOrEmpty(replyToken))
{
throw new ArgumentException($"{nameof(replyToken)} is null or empty.");
}
if (messages == null)
{
throw new ArgumentNullException(nameof(messages));
}
if (messages.Length == 0 || messages.Length > 5)
{
throw new ArgumentException($"{nameof(messages)} is required and max length is 5.");
}
await Post(MessageReplyApiPath, new LineReplyMessage
{
ReplyToken = replyToken,
Messages = messages.Select(x => (ILineMessage)new LineTextMessage { Text = x }).ToList()
});
}
}
}
+20
View File
@@ -0,0 +1,20 @@
using System;
using System.Threading.Tasks;
namespace LineMessaging
{
public partial class LineMessagingClient
{
private const string ProfileApiPath = "/v2/bot/profile/{0}";
public async Task<LineProfile> GetProfile(string userId)
{
if (string.IsNullOrEmpty(userId))
{
throw new ArgumentException($"{nameof(userId)} is null or empty.");
}
return await Get<LineProfile>(string.Format(ProfileApiPath, userId));
}
}
}
+98
View File
@@ -0,0 +1,98 @@
using System;
using Newtonsoft.Json;
using System.Threading.Tasks;
namespace LineMessaging
{
public partial class LineMessagingClient
{
private const string RichMenuApiPath = "/v2/bot/richmenu/{0}";
private const string RichMenusApiPath = "/v2/bot/richmenu";
private const string UsersRichMenuApiPath = "/v2/bot/user/{0}/richmenu";
private const string LinkUsersRichMenuApiPath = "/v2/bot/user/{0}/richmenu/{1}";
private const string RichMenuContentApiPath = "/v2/bot/richmenu/{0}/content";
public async Task<LineRichMenuResponse> GetRichMenu(string richMenuId)
{
if (string.IsNullOrEmpty(richMenuId))
{
throw new ArgumentException($"{nameof(richMenuId)} is null or empty.");
}
return await Get<LineRichMenuResponse>(string.Format(RichMenuApiPath, richMenuId));
}
public async Task<string> CreateRichMenu(LineRichMenu richMenu)
{
if (richMenu == null)
{
throw new ArgumentNullException(nameof(richMenu));
}
var response = await Post<RichMenuIdResponse>(RichMenusApiPath, richMenu);
return response?.RichMenuId;
}
public async Task DeleteRichMenu(string richMenuId)
{
if (string.IsNullOrEmpty(richMenuId))
{
throw new ArgumentException($"{nameof(richMenuId)} is null or empty.");
}
await Delete(string.Format(RichMenuApiPath, richMenuId));
}
public async Task<string> GetUsersRichMenuId(string userId)
{
if (string.IsNullOrEmpty(userId))
{
throw new ArgumentException($"{nameof(userId)} is null or empty.");
}
var response = await Get<RichMenuIdResponse>(string.Format(UsersRichMenuApiPath, userId));
return response?.RichMenuId;
}
public async Task LinkUsersRichMenu(string userId, string richMenuId)
{
if (string.IsNullOrEmpty(userId))
{
throw new ArgumentException($"{nameof(userId)} is null or empty.");
}
if (string.IsNullOrEmpty(richMenuId))
{
throw new ArgumentException($"{nameof(richMenuId)} is null or empty.");
}
await Post(string.Format(LinkUsersRichMenuApiPath, userId, richMenuId));
}
public async Task UnlinkUsersRichMenu(string userId)
{
if (string.IsNullOrEmpty(userId))
{
throw new ArgumentException($"{nameof(userId)} is null or empty.");
}
await Delete(string.Format(UsersRichMenuApiPath, userId));
}
public async Task<byte[]> GetRichMenuContent(string richMenuId)
{
if (string.IsNullOrEmpty(richMenuId))
{
throw new ArgumentException($"{nameof(richMenuId)} is null or empty.");
}
return await GetAsByteArray(string.Format(RichMenuContentApiPath, richMenuId));
}
internal class RichMenuIdResponse
{
[JsonProperty("richMenuId")]
internal string RichMenuId { get; set; }
}
}
}
+54
View File
@@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace LineMessaging
{
public partial class LineMessagingClient
{
private const string RoomMemberApiPath = "/v2/bot/room/{0}/member/{1}";
private const string RoomMembersApiPath = "/v2/bot/room/{0}/members/ids";
private const string LeaveRoomApiPath = "/v2/bot/room/{0}/leave";
public async Task<LineProfile> GetRoomMember(string roomId, string userId)
{
if (string.IsNullOrEmpty(roomId))
{
throw new ArgumentException($"{nameof(roomId)} is null or empty.");
}
if (string.IsNullOrEmpty(userId))
{
throw new ArgumentException($"{nameof(userId)} is null or empty.");
}
return await Get<LineProfile>(string.Format(RoomMemberApiPath, roomId, userId));
}
public async Task<LineMembers> GetRoomMembers(string roomId, string start = null)
{
if (string.IsNullOrEmpty(roomId))
{
throw new ArgumentException($"{nameof(roomId)} is null or empty.");
}
var query = new Dictionary<string, object>();
if (start != null)
{
query["start"] = start;
}
return await Get<LineMembers>(string.Format(RoomMembersApiPath, roomId), query);
}
public async Task LeaveRoom(string roomId)
{
if (string.IsNullOrEmpty(roomId))
{
throw new ArgumentException($"{nameof(roomId)} is null or empty.");
}
await Post(string.Format(LeaveRoomApiPath, roomId));
}
}
}
@@ -0,0 +1,34 @@
using System.Linq;
using Newtonsoft.Json;
namespace LineMessaging
{
public class LineErrorResponse
{
[JsonProperty("message")]
public string Message { get; set; }
[JsonProperty("details")]
public DetailObject[] Details { get; set; }
public class DetailObject
{
[JsonProperty("message")]
public string Message { get; set; }
[JsonProperty("property")]
public string Property { get; set; }
}
public override string ToString()
{
if (Details != null && Details.Any())
{
var details = string.Join(", ", Details.Select(x => $"Property {x.Property} {x.Message}"));
return $"{Message}. details: {details}";
}
return Message;
}
}
}
+13
View File
@@ -0,0 +1,13 @@
using Newtonsoft.Json;
namespace LineMessaging
{
public class LineMembers
{
[JsonProperty("memberIds")]
public string[] MemberIds { get; set; }
[JsonProperty("next")]
public string Next { get; set; }
}
}
@@ -0,0 +1,19 @@
using Newtonsoft.Json;
namespace LineMessaging
{
public class LineProfile
{
[JsonProperty("displayName")]
public string DisplayName { get; set; }
[JsonProperty("userId")]
public string UserId { get; set; }
[JsonProperty("pictureUrl")]
public string PictureUrl { get; set; }
[JsonProperty("statusMessage")]
public string StatusMessage { get; set; }
}
}
@@ -0,0 +1,15 @@
using System;
namespace LineMessaging
{
internal static class DateTimeExtensions
{
private static readonly DateTimeOffset UnixEpochDateTimeOffset = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero);
private static readonly DateTime UnixEpochDateTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
internal static DateTime FromUnixTime(this long unixTime)
{
return UnixEpochDateTime.AddMilliseconds(unixTime).ToLocalTime();
}
}
}
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace LineMessaging
{
internal static class DictionaryExtensions
{
internal static string ToQueryString(this Dictionary<string, object> source)
{
if (source == null) throw new ArgumentNullException();
if (source.Count == 0) return string.Empty;
return "?" + string.Join("&", source.Select(x => $"{x.Key}={x.Value}"));
}
}
}
+10
View File
@@ -0,0 +1,10 @@
using System;
namespace LineMessaging
{
internal static class LineConstants
{
internal static readonly Uri BaseUri = new Uri("https://api.line.me/");
internal static readonly Uri BaseDataUri = new Uri("https://api-data.line.me/");
}
}
+26
View File
@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>1.4.0</Version>
<Authors>Kiyoaki Tsurutani</Authors>
<Company></Company>
<Product />
<Description>LINE Messaging API Client Library for .NET (C#)</Description>
<Copyright>Kiyoaki Tsurutani</Copyright>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/kiyoaki/LineMessagingApi</PackageProjectUrl>
<RepositoryUrl>https://github.com/kiyoaki/LineMessagingApi</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>LINE, BOT</PackageTags>
<PackageReleaseNotes>Support .NET 6</PackageReleaseNotes>
<SignAssembly>false</SignAssembly>
<AssemblyOriginatorKeyFile>opensource.pfx</AssemblyOriginatorKeyFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>
</Project>
+185
View File
@@ -0,0 +1,185 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
namespace LineMessaging
{
public partial class LineMessagingClient
{
private const string ACCESS_TOKEN = "WFAyMvMEZ86cfMJIAzE+yklUZGpeS/jFYTeL9a9O35QR83oNMmwaUJfyEe48Kegadz0BArDdBoySxs479U1pwTHtlyH+Sm4jqlz8BwukX/Hsa4D1fX03Qn4zFu7TwPFKWFXnZbWq89Yg0iNzjpfTNwdB04t89/1O/w1cDnyilFU=";
private static readonly MediaTypeHeaderValue MediaTypeJson = MediaTypeHeaderValue.Parse("application/json; charset=utf-8");
private static readonly MediaTypeHeaderValue MediaTypeJpeg = MediaTypeHeaderValue.Parse("image/jpeg");
private static readonly MediaTypeHeaderValue MediaTypePng = MediaTypeHeaderValue.Parse("image/png");
private static readonly HttpClient HttpClient = new HttpClient
{
BaseAddress = LineConstants.BaseUri,
Timeout = TimeSpan.FromSeconds(10)
};
private static readonly HttpClient HttpDataClient = new HttpClient
{
BaseAddress = LineConstants.BaseDataUri,
Timeout = TimeSpan.FromSeconds(10)
};
private readonly AuthenticationHeaderValue accessTokenHeaderValue;
private JsonSerializerSettings serializerSettings;
public LineMessagingClient()
{
string accessToken = ACCESS_TOKEN;
if (string.IsNullOrEmpty(accessToken))
{
throw new ArgumentException($"{nameof(accessToken)} is null or empty.");
}
this.serializerSettings = new JsonSerializerSettings()
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore,
Formatting=Formatting.Indented
};
accessTokenHeaderValue = new AuthenticationHeaderValue("Bearer", accessToken);
}
private async Task<T> Get<T>(string path, Dictionary<string, object> query = null)
{
return await SendRequest<T>(HttpMethod.Get, path, query);
}
private async Task<T> Post<T>(string path, object body)
{
return await SendRequest<T>(HttpMethod.Post, path, null, body);
}
private async Task Post(string path, object body = null)
{
await SendRequest(HttpMethod.Post, path, null, body);
}
private async Task PostJpeg(string path, byte[] image)
{
await PostImage(path, "jpeg", image);
}
private async Task PostPng(string path, byte[] image)
{
await PostImage(path, "png", image);
}
private async Task Delete(string path)
{
await SendRequest(HttpMethod.Delete, path);
}
private async Task<byte[]> GetAsByteArray(string path)
{
using (var message = new HttpRequestMessage(HttpMethod.Get, path))
{
message.Headers.Authorization = accessTokenHeaderValue;
HttpResponseMessage response;
try
{
response = await HttpDataClient.SendAsync(message);
}
catch (TaskCanceledException)
{
throw new LineMessagingException(path, "Request Timeout");
}
await CheckStatusCode(path, response);
return await response.Content.ReadAsByteArrayAsync();
}
}
private async Task<T> SendRequest<T>(HttpMethod method, string path,
Dictionary<string, object> query = null, object body = null)
{
var responseJson = await SendRequest(method, path, query, body);
return JsonConvert.DeserializeObject<T>(responseJson);
}
private async Task PostImage(string path, string imageFormat, byte[] image)
{
using (var message = new HttpRequestMessage(HttpMethod.Post, path))
{
message.Content = new ByteArrayContent(image);
switch (imageFormat)
{
case "jpeg":
message.Content.Headers.ContentType = MediaTypeJpeg;
break;
case "png":
message.Content.Headers.ContentType = MediaTypePng;
break;
default:
throw new LineMessagingException(path, $"{imageFormat} is not supported.");
}
message.Headers.Authorization = accessTokenHeaderValue;
HttpResponseMessage response;
try
{
response = await HttpClient.SendAsync(message);
}
catch (TaskCanceledException)
{
throw new LineMessagingException(path, "Request Timeout");
}
}
}
private async Task<string> SendRequest(HttpMethod method, string path,
Dictionary<string, object> query = null, object body = null)
{
string queryString = null;
if (query != null)
{
queryString = query.ToQueryString();
}
using (var message = new HttpRequestMessage(method, path + queryString))
{
if (body != null)
{
string jsonContent = JsonConvert.SerializeObject(body, serializerSettings);
message.Content = new StringContent(jsonContent);
message.Content.Headers.ContentType = MediaTypeJson;
}
message.Headers.Authorization = accessTokenHeaderValue;
HttpResponseMessage response;
try
{
response = await HttpClient.SendAsync(message);
}
catch (TaskCanceledException)
{
throw new LineMessagingException(path, "Request Timeout");
}
await CheckStatusCode(path, response);
return await response.Content.ReadAsStringAsync();
}
}
private static async Task CheckStatusCode(string path, HttpResponseMessage response)
{
if (!response.IsSuccessStatusCode)
{
var responseJson = await response.Content.ReadAsStringAsync();
var error = JsonConvert.DeserializeObject<LineErrorResponse>(responseJson);
if (error != null)
{
throw new LineMessagingException(path, error);
}
throw new LineMessagingException(path,
$"Error has occurred. Response StatusCode:{response.StatusCode} ReasonPhrase:{response.ReasonPhrase}.");
}
}
}
}
+38
View File
@@ -0,0 +1,38 @@
using System;
namespace LineMessaging
{
public class LineMessagingException : Exception
{
public string Path { get; }
public LineOAuthErrorResponse OAuthErrorResponse { get; }
public LineErrorResponse ErrorResponse { get; }
public LineMessagingException(string path, LineErrorResponse errorResponse)
: base(errorResponse.ToString())
{
Path = path ?? throw new ArgumentNullException(nameof(path));
ErrorResponse = errorResponse;
}
public LineMessagingException(string path, LineOAuthErrorResponse oAuthErrorResponse)
: base(oAuthErrorResponse.ToString())
{
Path = path ?? throw new ArgumentNullException(nameof(path));
OAuthErrorResponse = oAuthErrorResponse;
}
public LineMessagingException(string path, string message)
: base(message)
{
Path = path ?? throw new ArgumentNullException(nameof(path));
}
public override string ToString()
{
return $"The request to {Path} has thrown an exception: {base.ToString()}";
}
}
}
+99
View File
@@ -0,0 +1,99 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
namespace LineMessaging
{
public class LineOAuthClient
{
private const string OAuthAccessTokenApiPath = "/v2/oauth/accessToken";
private const string OAuthRevokeTokenApiPath = "/v2/oauth/revoke";
private static readonly HttpClient HttpClient = new HttpClient
{
BaseAddress = LineConstants.BaseUri,
Timeout = TimeSpan.FromSeconds(10)
};
private readonly string channelId;
private readonly string channelSecret;
public LineOAuthClient(string channelId, string channelSecret)
{
if (string.IsNullOrEmpty(channelId))
{
throw new ArgumentException($"{nameof(channelId)} is null or empty.");
}
if (string.IsNullOrEmpty(channelSecret))
{
throw new ArgumentException($"{nameof(channelSecret)} is null or empty.");
}
this.channelId = channelId;
this.channelSecret = channelSecret;
}
public async Task<LineOAuthTokenResponse> GetAccessToken()
{
using (var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
{ "grant_type", "client_credentials" },
{ "client_id", channelId },
{ "client_secret", channelSecret }
}))
{
try
{
var response = await HttpClient.PostAsync(OAuthAccessTokenApiPath, content);
var responseJson = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
var error = JsonConvert.DeserializeObject<LineOAuthErrorResponse>(responseJson);
if (error != null)
{
throw new LineMessagingException(OAuthAccessTokenApiPath, error);
}
throw new LineMessagingException(OAuthAccessTokenApiPath,
$"Error has occurred. Response StatusCode:{response.StatusCode} ReasonPhrase:{response.ReasonPhrase}.");
}
return JsonConvert.DeserializeObject<LineOAuthTokenResponse>(responseJson);
}
catch (TaskCanceledException)
{
throw new LineMessagingException(OAuthAccessTokenApiPath, "Request Timeout");
}
}
}
public async Task RevokeAccessToken(string accessToken)
{
using (var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
{ "access_token", accessToken }
}))
{
try
{
var response = await HttpClient.PostAsync(OAuthRevokeTokenApiPath, content);
if (!response.IsSuccessStatusCode)
{
var responseJson = await response.Content.ReadAsStringAsync();
var error = JsonConvert.DeserializeObject<LineOAuthErrorResponse>(responseJson);
if (error != null)
{
throw new LineMessagingException(OAuthAccessTokenApiPath, error);
}
throw new LineMessagingException(OAuthAccessTokenApiPath,
$"Error has occurred. Response StatusCode:{response.StatusCode} ReasonPhrase:{response.ReasonPhrase}.");
}
}
catch (TaskCanceledException)
{
throw new LineMessagingException(OAuthAccessTokenApiPath, "Request Timeout");
}
}
}
}
}
+85
View File
@@ -0,0 +1,85 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace LineMessaging
{
public class LineWebhookRequest
{
private readonly byte[] secret;
private readonly HttpRequestMessage request;
private string contentJson;
private LineWebhookContent content;
public LineWebhookRequest(string channelSecret, HttpRequestMessage request)
{
if (string.IsNullOrEmpty(channelSecret))
{
throw new ArgumentException($"{nameof(channelSecret)} is null or empty.");
}
secret = Encoding.UTF8.GetBytes(channelSecret);
this.request = request;
}
public async Task<bool> IsValid()
{
if (!request.Headers.TryGetValues("X-Line-Signature", out IEnumerable<string> headers))
{
return false;
}
var signature = headers.FirstOrDefault();
if (signature == null)
{
return false;
}
contentJson = await request.Content.ReadAsStringAsync();
var body = await request.Content.ReadAsByteArrayAsync();
using (var hmacsha256 = new HMACSHA256(secret))
{
if (signature != Convert.ToBase64String(hmacsha256.ComputeHash(body)))
{
return false;
}
}
return true;
}
public async Task<string> GetContentJson()
{
if (contentJson != null)
{
return contentJson;
}
contentJson = await request.Content.ReadAsStringAsync();
return contentJson;
}
public async Task<LineWebhookContent> GetContent()
{
if (content != null)
{
return content;
}
contentJson = await GetContentJson();
if (contentJson == null)
{
return null;
}
content = JsonConvert.DeserializeObject<LineWebhookContent>(contentJson);
return content;
}
}
}
@@ -0,0 +1,76 @@
using Newtonsoft.Json;
namespace LineMessaging
{
public interface ILineAction
{
[JsonProperty("type")]
ActionType Type { get; }
}
public class PostbackAction : ILineAction
{
[JsonProperty("type")]
public ActionType Type => ActionType.Postback;
[JsonProperty("label")]
public string Label { get; set; }
[JsonProperty("data")]
public string Data { get; set; }
[JsonProperty("displayText")]
public string DisplayText { get; set; }
[JsonProperty("inputOption")]
public string InputOption { get; set; }
[JsonProperty("fillInText")]
public string FillInText { get; set; }
}
public class MessageAction : ILineAction
{
[JsonProperty("type")]
public ActionType Type => ActionType.Message;
[JsonProperty("label")]
public string Label { get; set; }
[JsonProperty("text")]
public string Text { get; set; }
}
public class UriAction : ILineAction
{
[JsonProperty("type")]
public ActionType Type => ActionType.Uri;
[JsonProperty("label")]
public string Label { get; set; }
[JsonProperty("uri")]
public string Uri { get; set; }
}
public class DatetimepickerAction : ILineAction
{
[JsonProperty("type")]
public ActionType Type => ActionType.Datetimepicker;
[JsonProperty("label")]
public string Label { get; set; }
[JsonProperty("data")]
public string Data { get; set; }
[JsonProperty("mode")]
public string Mode { get; set; }
[JsonProperty("initial")]
public string Initial { get; set; }
[JsonProperty("max")]
public string Max { get; set; }
[JsonProperty("min")]
public string Min { get; set; }
}
}
@@ -0,0 +1,19 @@
using Newtonsoft.Json;
namespace LineMessaging
{
public class LineAreaBounds
{
[JsonProperty("x")]
public int X { get; set; }
[JsonProperty("y")]
public int Y { get; set; }
[JsonProperty("width")]
public int Width { get; set; }
[JsonProperty("height")]
public int Height { get; set; }
}
}
@@ -0,0 +1,16 @@
using Newtonsoft.Json;
namespace LineMessaging
{
public class LineAudioMessage : ILineMessage
{
[JsonProperty("type")]
public MessageType Type => MessageType.Audio;
[JsonProperty("originalContentUrl")]
public string OriginalContentUrl { get; set; }
[JsonProperty("duration")]
public long Duration { get; set; }
}
}
@@ -0,0 +1,78 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using System.Runtime.Serialization;
namespace LineMessaging
{
[JsonConverter(typeof(StringEnumConverter))]
public enum FlexContentType
{
[EnumMember(Value = "bubble")]
Bubble,
}
public class LineFlexMessage : ILineMessage
{
public LineFlexMessage()
{
Contents = new LineFlexContent();
//Contents.Body = new LineFlexBox();
}
[JsonProperty("type")]
public MessageType Type => MessageType.Flex;
[JsonProperty("altText")]
public string AltText { get; set; }
[JsonProperty("contents")]
public LineFlexContent Contents { get; set; }
}
public class LineFlexContent
{
[JsonProperty("type")]
public FlexContentType Type => FlexContentType.Bubble;
[JsonProperty("header")]
public ILineFlexObject Header { get; set; }
[JsonProperty("hero")]
public ILineFlexObject Hero { get; set; }
[JsonProperty("body")]
public ILineFlexObject Body { get; set; }
[JsonProperty("footer")]
public ILineFlexObject Footer { get; set; }
public ICollection<ILineFlexObject> InitHeader()
{
var FlexBox = new LineFlexBox();
this.Header = FlexBox;
FlexBox.PaddingAll = "sm";
return FlexBox.Contents;
}
public ICollection<ILineFlexObject> InitHero()
{
var FlexBox = new LineFlexBox();
this.Hero = FlexBox;
return FlexBox.Contents;
}
public ICollection<ILineFlexObject> InitBody()
{
var FlexBox = new LineFlexBox();
FlexBox.PaddingBottom = "none";
this.Body = FlexBox;
return FlexBox.Contents;
}
public ICollection<ILineFlexObject> InitFooter()
{
var FlexBox = new LineFlexBox();
this.Footer = FlexBox;
FlexBox.PaddingAll = "none";
return FlexBox.Contents;
}
}
}
+193
View File
@@ -0,0 +1,193 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using static System.Net.Mime.MediaTypeNames;
namespace LineMessaging
{
public interface ILineFlexObject
{
FlexObjectType Type { get; }
}
[JsonConverter(typeof(StringEnumConverter))]
public enum FlexObjectType
{
[EnumMember(Value = "box")]
Box,
[EnumMember(Value = "button")]
Button,
[EnumMember(Value = "image")]
Image,
[EnumMember(Value = "video")]
Video,
[EnumMember(Value = "icon")]
Icon,
[EnumMember(Value = "text")]
Text,
[EnumMember(Value = "span")]
Span,
[EnumMember(Value = "separator")]
Separator,
[EnumMember(Value = "filler")]
Filler,
}
[JsonConverter(typeof(StringEnumConverter))]
public enum FlexObjectBoxLayout
{
[EnumMember(Value = "vertical")]
Vertical,
[EnumMember(Value = "horizontal")]
Horizontal,
[EnumMember(Value = "baseline")]
Baseline,
}
[JsonConverter(typeof(StringEnumConverter))]
public enum FlexObjectButtonStype
{
[EnumMember(Value = "link")]
Link,
[EnumMember(Value = "primary")]
Primary,
[EnumMember(Value = "secondary")]
Secondary,
}
[JsonConverter(typeof(StringEnumConverter))]
public enum FlexObjectSize
{
xxs,
xs,
sm,
md,
lg,
xl,
xxl,
[EnumMember(Value = "3xl")]
xxxl,
[EnumMember(Value = "4xl")]
xxxxl,
[EnumMember(Value = "5xl")]
xxxxxl,
full
}
[JsonConverter(typeof(StringEnumConverter))]
public enum FlexObjectTextWeidht
{
[EnumMember(Value = "regular")]
Regular,
[EnumMember(Value = "bold")]
Bold,
}
public class LineFlexBox : ILineFlexObject
{
public LineFlexBox()
{
Layout = FlexObjectBoxLayout.Vertical;
Contents = new List<ILineFlexObject>();
}
public FlexObjectType Type => FlexObjectType.Box;
public FlexObjectBoxLayout Layout { get; set; }
public ICollection<ILineFlexObject> Contents { get; set; }
//public FlexObjectSize Height { get; set; }
//public string JustifyContent => "space-evenly";
//public string AlignItems => "center";
public string PaddingAll { get; set; }
public string PaddingTop { get; set; }
public string PaddingBottom{ get; set; }
public string PaddingStart { get; set; }
public string PaddingEnd { get; set; }
}
public class LineFlexButton : ILineFlexObject
{
public FlexObjectType Type => FlexObjectType.Button;
[JsonRequired]
public ILineAction Action { get; set; }
public FlexObjectButtonStype Style { get; set; }
//public FlexObjectSize Size { get; set; }
}
public class LineFlexImage : ILineFlexObject
{
public LineFlexImage(string url)
{
AspectRatio = "1.91:1";
Url = url;
Size = FlexObjectSize.md;
}
public FlexObjectType Type => FlexObjectType.Image;
[JsonRequired]
public string Url { get; set; }
public FlexObjectSize Size { get; set; }
public string AspectRatio { get; set; }
public ILineAction Action { get; set; }
}
public class LineFlexText : ILineFlexObject
{
public LineFlexText()
{
Size = FlexObjectSize.md;
Weight = FlexObjectTextWeidht.Regular;
}
public LineFlexText(string text)
{
Text = text;
Size = FlexObjectSize.md;
Weight = FlexObjectTextWeidht.Regular;
}
public FlexObjectType Type => FlexObjectType.Text;
[JsonRequired]
public string Text { get; set; }
public int Flex { get; set; }
//public ILineAction Action { get; set; }
//public FlexObjectButtonStype Style { get; set; }
public FlexObjectSize Size { get; set; }
public FlexObjectSize? OffsetTop { get; set; }
public FlexObjectSize? OffsetBottom { get; set; }
public FlexObjectSize? OffsetStart { get; set; }
public FlexObjectSize? OffsetEnd { get; set; }
public FlexObjectTextWeidht Weight { get; set; }
public string Color { get; set; }
/// <summary>
/// true to wrap text. The default value is false. If set to true, you can use a new line character (\n) to begin on a new line. For more information, see Wrapping text in the Messaging API documentation.
/// </summary>
public bool Wrap { get; set; }
/// <summary>
/// Style of the text. Specify one of these values: normal: Normal italic: Italic The default value is normal.
/// </summary>
public string Style { get; set; }
/// <summary>
/// Decoration of the text. Specify one of these values:
// none: No decoration
// underline: Underline
// line-through: Strikethrough
// The default value is none.
/// </summary>
public string Decoration { get; set; }
/// <summary>
/// start: Left-aligned, end: Right-aligned, center: Center-aligned(default value)
/// </summary>
public string Align { get; set; }
}
public class LineFlexSeparator : ILineFlexObject
{
public LineFlexSeparator()
{
Margin = FlexObjectSize.md;
}
public FlexObjectType Type => FlexObjectType.Separator;
public FlexObjectSize Margin { get; set; }
}
}
@@ -0,0 +1,16 @@
using Newtonsoft.Json;
namespace LineMessaging
{
public class LineImageMessage : ILineMessage
{
[JsonProperty("type")]
public MessageType Type => MessageType.Image;
[JsonProperty("originalContentUrl")]
public string OriginalContentUrl { get; set; }
[JsonProperty("previewImageUrl")]
public string PreviewImageUrl { get; set; }
}
}
@@ -0,0 +1,55 @@
using Newtonsoft.Json;
namespace LineMessaging
{
public class LineImagemapMessage : ILineMessage
{
[JsonProperty("type")]
public MessageType Type => MessageType.Imagemap;
[JsonProperty("baseUrl")]
public string BaseUrl { get; set; }
[JsonProperty("altText")]
public string AltText { get; set; }
[JsonProperty("baseSize")]
public LineSizeObject BaseSize { get; set; }
[JsonProperty("actions")]
public IAction[] Actions { get; set; }
public interface IAction
{
[JsonProperty("type")]
ActionType Type { get; }
[JsonProperty("area")]
LineAreaBounds Area { get; set; }
}
public class LinkActionObject : IAction
{
[JsonProperty("type")]
public ActionType Type => ActionType.Uri;
[JsonProperty("linkUri")]
public string LinkUri { get; set; }
[JsonProperty("area")]
public LineAreaBounds Area { get; set; }
}
public class MessageActionObject : IAction
{
[JsonProperty("type")]
public ActionType Type => ActionType.Message;
[JsonProperty("text")]
public string Text { get; set; }
[JsonProperty("area")]
public LineAreaBounds Area { get; set; }
}
}
}
@@ -0,0 +1,22 @@
using Newtonsoft.Json;
namespace LineMessaging
{
public class LineLocationMessage : ILineMessage
{
[JsonProperty("type")]
public MessageType Type => MessageType.Location;
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("address")]
public string Address { get; set; }
[JsonProperty("latitude")]
public double Latitude { get; set; }
[JsonProperty("longitude")]
public double Longitude { get; set; }
}
}
@@ -0,0 +1,73 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System.Runtime.Serialization;
namespace LineMessaging
{
[JsonConverter(typeof(StringEnumConverter))]
public enum MessageType
{
[EnumMember(Value = "text")]
Text,
[EnumMember(Value = "image")]
Image,
[EnumMember(Value = "video")]
Video,
[EnumMember(Value = "audio")]
Audio,
[EnumMember(Value = "file")]
File,
[EnumMember(Value = "location")]
Location,
[EnumMember(Value = "sticker")]
Sticker,
[EnumMember(Value = "imagemap")]
Imagemap,
[EnumMember(Value = "template")]
Template,
[EnumMember(Value = "flex")]
Flex
}
[JsonConverter(typeof(StringEnumConverter))]
public enum TemplateType
{
[EnumMember(Value = "buttons")]
Buttons,
[EnumMember(Value = "confirm")]
Confirm,
[EnumMember(Value = "carousel")]
Carousel,
[EnumMember(Value = "image_carousel")]
ImageCarousel,
}
[JsonConverter(typeof(StringEnumConverter))]
public enum ActionType
{
[EnumMember(Value = "postback")]
Postback,
[EnumMember(Value = "message")]
Message,
[EnumMember(Value = "uri")]
Uri,
[EnumMember(Value = "datetimepicker")]
Datetimepicker
}
}
@@ -0,0 +1,15 @@
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Linq;
namespace LineMessaging
{
public class LineMulticastMessage
{
[JsonProperty("to")]
public IEnumerable<string> To { get; set; }
[JsonProperty("messages")]
public IEnumerable<ILineMessage> Messages { get; set; }
}
}
@@ -0,0 +1,15 @@
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Linq;
namespace LineMessaging
{
public class LinePushMessage
{
[JsonProperty("to")]
public string To { get; set; }
[JsonProperty("messages")]
public IEnumerable<ILineMessage> Messages { get; set; }
}
}
@@ -0,0 +1,14 @@
using Newtonsoft.Json;
using System.Collections.Generic;
namespace LineMessaging
{
public class LineReplyMessage
{
[JsonProperty("replyToken")]
public string ReplyToken { get; set; }
[JsonProperty("messages")]
public IEnumerable<ILineMessage> Messages { get; set; }
}
}
+31
View File
@@ -0,0 +1,31 @@
using Newtonsoft.Json;
namespace LineMessaging
{
public class LineRichMenu
{
[JsonProperty("size")]
public LineSizeObject Size { get; set; }
[JsonProperty("selected")]
public bool Selected { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("charBarText")]
public string CharBarText { get; set; }
[JsonProperty("areas")]
public BoundsObject[] Areas { get; set; }
public class BoundsObject
{
[JsonProperty("bounds")]
public LineAreaBounds Bounds { get; set; }
[JsonProperty("action")]
public ILineAction Action { get; set; }
}
}
}
@@ -0,0 +1,10 @@
using Newtonsoft.Json;
namespace LineMessaging
{
public class LineRichMenuResponse : LineRichMenu
{
[JsonProperty("richMenuId")]
public string RichMenuId { get; set; }
}
}
@@ -0,0 +1,13 @@
using Newtonsoft.Json;
namespace LineMessaging
{
public class LineSizeObject
{
[JsonProperty("width")]
public int Width { get; set; }
[JsonProperty("height")]
public int Height { get; set; }
}
}
@@ -0,0 +1,16 @@
using Newtonsoft.Json;
namespace LineMessaging
{
public class LineStickerMessage : ILineMessage
{
[JsonProperty("type")]
public MessageType Type => MessageType.Sticker;
[JsonProperty("packageId")]
public string PackageId { get; set; }
[JsonProperty("stickerId")]
public string StickerId { get; set; }
}
}
+59
View File
@@ -0,0 +1,59 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LineMessaging
{
// Root myDeserializedClass = JsonConvert.DeserializeObject<Root>(myJsonResponse);
public interface ITemplateObject
{
[JsonProperty("type")]
TemplateType Type { get; }
}
public class ButtonTemplateObject : ITemplateObject
{
public ButtonTemplateObject()
{
Actions = new List<ILineAction>();
ImageSize = "cover";
ImageBackgroundColor = "#FFFFFF";
ImageAspectRatio = "rectangle";
}
[JsonProperty("type")]
public TemplateType Type => TemplateType.Buttons;
[JsonProperty("thumbnailImageUrl")]
public string ThumbnailImageUrl { get; set; }
[JsonProperty("imageAspectRatio")]
public string ImageAspectRatio { get; set; }
[JsonProperty("imageSize")]
public string ImageSize { get; set; }
[JsonProperty("imageBackgroundColor")]
public string ImageBackgroundColor { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("text")]
public string Text { get; set; }
[JsonProperty("defaultAction")]
public ILineAction DefaultAction { get; set; }
[JsonProperty("actions")]
public List<ILineAction> Actions { get; set; }
}
}
@@ -0,0 +1,22 @@
using Newtonsoft.Json;
namespace LineMessaging
{
public class LineTemplateMessage<T> : ILineMessage where T: ITemplateObject,new ()
{
public LineTemplateMessage()
{
Template = new T();
}
[JsonProperty("type")]
public MessageType Type => MessageType.Template;
[JsonProperty("altText")]
public string AltText { get; set; }
[JsonProperty("template")]
public T Template { get; set; }
}
}
@@ -0,0 +1,76 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
namespace LineMessaging
{
public class LineTextMessage : ILineMessage
{
[JsonProperty("type")]
public MessageType Type => MessageType.Text;
[JsonProperty("text")]
public string Text { get; set; }
[JsonProperty("emojis")]
public List<Emoji> Emojis { get; set; }
public void AddEmoji(int index, string productId, string emojiId)
{
if (Emojis == null) Emojis = new List<Emoji>();
Emojis.Add(new Emoji()
{
Index = index,
ProductId = productId,
EmojiId = emojiId
});
}
public void AddEmoji(int indexStart, int indexEnd, string productId, string emojiId)
{
if (Emojis == null) Emojis = new List<Emoji>();
for (int i = indexStart; i < indexEnd; i++)
{
Emojis.Add(new Emoji()
{
Index = i,
ProductId = productId,
EmojiId = emojiId
});
}
}
public void AddEmoji(string replaceSymbol, string productId, string emojiId)
{
if (Emojis == null) Emojis = new List<Emoji>();
this.Text = this.Text.Replace(replaceSymbol, "|");
foreach (var i in AllIndexesOf(this.Text, "|"))
{
Emojis.Add(new Emoji()
{
Index = i,
ProductId = productId,
EmojiId = emojiId
});
}
this.Text = this.Text.Replace("|", "$");
}
private IEnumerable<int> AllIndexesOf(string str, string searchstring)
{
int minIndex = str.IndexOf(searchstring);
while (minIndex != -1)
{
yield return minIndex;
minIndex = str.IndexOf(searchstring, minIndex + searchstring.Length);
}
}
}
public class Emoji
{
[JsonProperty("index")]
public int Index { get; set; }
[JsonProperty("productId")]
public string ProductId { get; set; }
[JsonProperty("emojiId")]
public string EmojiId { get; set; }
}
}
@@ -0,0 +1,16 @@
using Newtonsoft.Json;
namespace LineMessaging
{
public class LineVideoMessage : ILineMessage
{
[JsonProperty("type")]
public MessageType Type => MessageType.Video;
[JsonProperty("originalContentUrl")]
public string OriginalContentUrl { get; set; }
[JsonProperty("previewImageUrl")]
public string PreviewImageUrl { get; set; }
}
}
@@ -0,0 +1,9 @@
using Newtonsoft.Json;
namespace LineMessaging
{
public interface ILineMessage
{
MessageType Type { get; }
}
}
@@ -0,0 +1,23 @@
using Newtonsoft.Json;
using System;
namespace LineMessaging
{
public class LineOAuthTokenResponse
{
[JsonProperty("access_token")]
public string AccessToken { get; set; }
[JsonProperty("expires_in")]
public long UnixtimeExpiresIn { get; set; }
[JsonIgnore]
public DateTime ExpiresIn
{
get { return UnixtimeExpiresIn.FromUnixTime(); }
}
[JsonProperty("token_type")]
public string TokenType { get; set; } = "Bearer";
}
}
@@ -0,0 +1,18 @@
using Newtonsoft.Json;
namespace LineMessaging
{
public class LineOAuthErrorResponse
{
[JsonProperty("error")]
public string Error { get; set; }
[JsonProperty("error_description")]
public string Description { get; set; }
public override string ToString()
{
return $"{Error}. description: {Description}.";
}
}
}
@@ -0,0 +1,57 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System.Runtime.Serialization;
namespace LineMessaging
{
[JsonConverter(typeof(StringEnumConverter))]
public enum WebhookRequestEventType
{
[EnumMember(Value = "message")]
Message,
[EnumMember(Value = "follow")]
Follow,
[EnumMember(Value = "unfollow")]
Unfollow,
[EnumMember(Value = "join")]
Join,
[EnumMember(Value = "leave")]
Leave,
[EnumMember(Value = "postback")]
Postback,
[EnumMember(Value = "beacon")]
Beacon
}
[JsonConverter(typeof(StringEnumConverter))]
public enum WebhookRequestSourceType
{
[EnumMember(Value = "user")]
User,
[EnumMember(Value = "group")]
Group,
[EnumMember(Value = "room")]
Room
}
[JsonConverter(typeof(StringEnumConverter))]
public enum WebhookRequestBeaconType
{
[EnumMember(Value = "enter")]
Enter,
[EnumMember(Value = "leave")]
Leave,
[EnumMember(Value = "banner")]
Banner
}
}
@@ -0,0 +1,125 @@
using Newtonsoft.Json;
using System;
namespace LineMessaging
{
public class LineWebhookContent
{
[JsonProperty("events")]
public Event[] Events { get; set; }
public class Event
{
[JsonProperty("type")]
public WebhookRequestEventType Type { get; set; }
[JsonProperty("timestamp")]
public long UnixTimestamp { get; set; }
[JsonIgnore]
public DateTime Timestamp
{
get { return UnixTimestamp.FromUnixTime(); }
}
[JsonProperty("replyToken")]
public string ReplyToken { get; set; }
[JsonProperty("source")]
public SourceObject Source { get; set; }
[JsonProperty("message")]
public MessageObject Message { get; set; }
[JsonProperty("postback")]
public PostbackObject Postback { get; set; }
[JsonProperty("beacon")]
public BeaconObject Beacon { get; set; }
public class SourceObject
{
[JsonProperty("type")]
public WebhookRequestSourceType Type { get; set; }
[JsonProperty("userId")]
public string UserId { get; set; }
[JsonProperty("groupId")]
public string GroupId { get; set; }
[JsonProperty("roomId")]
public string RoomId { get; set; }
}
public class MessageObject
{
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("type")]
public MessageType Type { get; set; }
[JsonProperty("text")]
public string Text { get; set; }
[JsonProperty("fileName")]
public string FileName { get; set; }
[JsonProperty("fileSize")]
public long? FileSize { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("address")]
public string Address { get; set; }
[JsonProperty("latitude")]
public double? Latitude { get; set; }
[JsonProperty("longitude")]
public double? Longitude { get; set; }
[JsonProperty("packageId")]
public string PackageId { get; set; }
[JsonProperty("stickerId")]
public string StickerId { get; set; }
}
public class PostbackObject
{
[JsonProperty("data")]
public string Data { get; set; }
[JsonProperty("params")]
public PostbackParams Params { get; set; }
public class PostbackParams
{
[JsonProperty("date")]
public string Date { get; set; }
[JsonProperty("time")]
public string Time { get; set; }
[JsonProperty("datetime")]
public string Datetime { get; set; }
}
}
public class BeaconObject
{
[JsonProperty("hwid")]
public string HardwareId { get; set; }
[JsonProperty("type")]
public WebhookRequestBeaconType Type { get; set; }
[JsonProperty("dm")]
public string DeviceMessage { get; set; }
}
}
}
}