Skip to content

Commit

Permalink
画像ファイル対応 (LINE -> Slack) (#12)
Browse files Browse the repository at this point in the history
* 画像ファイル対応 (LINE -> Slack )

* fix
  • Loading branch information
YDKK authored Sep 27, 2020
1 parent f0c243b commit a72e6dc
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 28 deletions.
52 changes: 52 additions & 0 deletions SlackLineBridge/Controllers/ProxyController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Mime;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Net.Http.Headers;
using SlackLineBridge.Services;

namespace SlackLineBridge.Controllers
{
[Route("[controller]")]
[ApiController]
public class ProxyController : ControllerBase
{
IHttpClientFactory _clientFactory;
string _lineChannelSecret;
public ProxyController(IHttpClientFactory clientFactory, string lineChannelSecret)
{
_clientFactory = clientFactory;
_lineChannelSecret = lineChannelSecret;
}

[HttpGet("line/{token}/{id}")]
public async Task<IActionResult> Line(string id, string token)
{
if (token != LineMessageProcessingService.GetHMACHex(id, _lineChannelSecret))
{
return new StatusCodeResult((int)HttpStatusCode.Forbidden);
}

var client = _clientFactory.CreateClient("Line");

var result = await client.GetAsync($"https://api-data.line.me/v2/bot/message/{id}/content");
if (result.IsSuccessStatusCode)
{
var stream = await result.Content.ReadAsStreamAsync();
var contentType = result.Content.Headers.GetValues("Content-Type").First();

return new FileStreamResult(stream, contentType);
}
else
{
return new StatusCodeResult((int)result.StatusCode);
}
}
}
}
6 changes: 3 additions & 3 deletions SlackLineBridge/Controllers/WebhookController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ public class WebhookController : ControllerBase
private readonly LineChannels _lineChannels;
private readonly SlackLineBridges _bridges;
private readonly IHttpClientFactory _clientFactory;
private readonly ConcurrentQueue<(string signature, string body)> _lineRequestQueue;
private readonly ConcurrentQueue<(string signature, string body, string host)> _lineRequestQueue;

public WebhookController(
ILogger<WebhookController> logger,
IOptionsSnapshot<SlackChannels> slackChannels,
IOptionsSnapshot<LineChannels> lineChannels,
IOptionsSnapshot<SlackLineBridges> bridges,
ConcurrentQueue<(string signature, string body)> lineRequestQueue,
ConcurrentQueue<(string signature, string body, string host)> lineRequestQueue,
IHttpClientFactory clientFactory)
{
_logger = logger;
Expand Down Expand Up @@ -134,7 +134,7 @@ public async Task<StatusCodeResult> Line()

using var reader = new StreamReader(Request.Body);

_lineRequestQueue.Enqueue((Request.Headers["X-Line-Signature"], await reader.ReadToEndAsync()));
_lineRequestQueue.Enqueue((Request.Headers["X-Line-Signature"], await reader.ReadToEndAsync(), Request.Host.ToString()));

return Ok();
}
Expand Down
63 changes: 39 additions & 24 deletions SlackLineBridge/Services/LineMessageProcessingService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using System.Linq;
using System.Net.Http;
using System.Security.Cryptography;
using System.Security.Policy;
using System.Text;
using System.Text.Json;
using System.Threading;
Expand All @@ -24,15 +25,15 @@ public class LineMessageProcessingService : BackgroundService
private readonly IOptionsMonitor<SlackLineBridges> _bridges;
private readonly IHttpClientFactory _clientFactory;
private readonly ILogger<LineMessageProcessingService> _logger;
private readonly ConcurrentQueue<(string signature, string body)> _queue;
private readonly ConcurrentQueue<(string signature, string body, string host)> _queue;
private readonly string _lineChannelSecret;

public LineMessageProcessingService(
IOptionsMonitor<SlackChannels> slackChannels,
IOptionsMonitor<LineChannels> lineChannels,
IOptionsMonitor<SlackLineBridges> bridges,
IHttpClientFactory clientFactory,
ConcurrentQueue<(string signature, string body)> lineRequestQueue,
ConcurrentQueue<(string signature, string body, string host)> lineRequestQueue,
string lineChannelSecret,
ILogger<LineMessageProcessingService> logger)
{
Expand All @@ -56,7 +57,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Processing request from line: " + request.body);

var signature = GetHMAC(request.body, _lineChannelSecret);
var signature = GetHMACBase64(request.body, _lineChannelSecret);
_logger.LogDebug($"LINE signature check (expected:{request.signature}, calculated:{signature})");
if (request.signature != signature)
{
Expand Down Expand Up @@ -88,17 +89,33 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)

var message = e.GetProperty("message");
var type = message.GetProperty("type").GetString();
var text = type switch
var text = "";
string imageUrl = null;
var id = "";
if (message.TryGetProperty("id", out var idElement))
{
"text" => message.GetProperty("text").GetString(),
_ => $"<{type}>",
};
id = idElement.GetString();
}

string stickerUrl = null;
if (type == "sticker")
switch (type)
{
var stickerId = message.GetProperty("stickerId").GetString();
stickerUrl = $"https://stickershop.line-scdn.net/stickershop/v1/sticker/{stickerId}/android/sticker.png";
case "text":
text = message.GetProperty("text").GetString();
break;
case "sticker":
var stickerId = message.GetProperty("stickerId").GetString();
imageUrl = $"https://stickershop.line-scdn.net/stickershop/v1/sticker/{stickerId}/android/sticker.png";
break;
case "image":
imageUrl = $"https://{request.host}/proxy/line/{GetHMACHex(id, _lineChannelSecret)}/{id}";
break;
default:
text = $"<{type}>";
if (!string.IsNullOrEmpty(id))
{
text += $"\nhttps://{request.host}/proxy/line/{GetHMACHex(id, _lineChannelSecret)}/{id}";
}
break;
}

foreach (var bridge in bridges)
Expand All @@ -110,7 +127,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
continue;
}

await SendToSlack(slackChannel.WebhookUrl, slackChannel.ChannelId, pictureUrl, userName, text, stickerUrl);
await SendToSlack(slackChannel.WebhookUrl, slackChannel.ChannelId, pictureUrl, userName, text, imageUrl);
}
}
break;
Expand All @@ -136,7 +153,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
_logger.LogDebug($"LineMessageProcessing background task is stopped.");
}

private async Task SendToSlack(string webhookUrl, string channelId, string pictureUrl, string userName, string text, string stickerUrl)
private async Task SendToSlack(string webhookUrl, string channelId, string pictureUrl, string userName, string text, string imageUrl)
{
var client = _clientFactory.CreateClient();

Expand All @@ -153,13 +170,13 @@ private async Task SendToSlack(string webhookUrl, string channelId, string pictu
message.icon_url = pictureUrl;
}

if (!string.IsNullOrEmpty(stickerUrl))
if (!string.IsNullOrEmpty(imageUrl))
{
message.blocks = new[]{new
{
type = "image",
image_url = stickerUrl,
alt_text = "sticker"
image_url = imageUrl,
alt_text = "image"
} };
}

Expand Down Expand Up @@ -246,21 +263,19 @@ private string GetLineEventSourceId(JsonElement e)
return resultProfile;
}

private static string GetHMAC(string text, string key)
private static byte[] CalcHMAC(string text, string key)
{
var encoding = new UTF8Encoding();

var textBytes = encoding.GetBytes(text);
var keyBytes = encoding.GetBytes(key);

byte[] hashBytes;
using var hash = new HMACSHA256(keyBytes);
return hash.ComputeHash(textBytes);
}

using (var hash = new HMACSHA256(keyBytes))
{
hashBytes = hash.ComputeHash(textBytes);
}
private static string GetHMACBase64(string text, string key) => Convert.ToBase64String(CalcHMAC(text, key));

return Convert.ToBase64String(hashBytes);
}
public static string GetHMACHex(string text, string key) => BitConverter.ToString(CalcHMAC(text, key)).Replace("-", "").ToLower();
}
}
2 changes: 2 additions & 0 deletions SlackLineBridge/SlackLineBridge.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@

<ItemGroup>
<PackageReference Include="AWS.Logger.AspNetCore" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.0.0" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.9.5" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="3.0.0" />
</ItemGroup>

</Project>
2 changes: 1 addition & 1 deletion SlackLineBridge/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public void ConfigureServices(IServiceCollection services)
services.Configure<SlackChannels>(x => x.Channels = Configuration.GetSection("slackChannels").Get<SlackChannel[]>());
services.Configure<LineChannels>(x => x.Channels = Configuration.GetSection("lineChannels").Get<LineChannel[]>());
services.Configure<SlackLineBridges>(x => x.Bridges = Configuration.GetSection("slackLineBridges").Get<Models.Configurations.SlackLineBridge[]>());
services.AddSingleton<ConcurrentQueue<(string signature, string body)>>(); //lineRequestQueue
services.AddSingleton<ConcurrentQueue<(string signature, string body, string host)>>(); //lineRequestQueue
services.AddSingleton(Configuration["lineChannelSecret"]);
services.AddHostedService<LineMessageProcessingService>();
services.AddHostedService<BackgroundAccessingService>();
Expand Down

0 comments on commit a72e6dc

Please sign in to comment.