Skip to content

Commit

Permalink
Merge pull request #278 from kufu/qr-code
Browse files Browse the repository at this point in the history
ビル来客システムのバージョンアップに対応し、QRコードバージョンが利用できるようにした
  • Loading branch information
j-o-lantern0422 authored Dec 20, 2024
2 parents 3702354 + 899210e commit 578c4ef
Show file tree
Hide file tree
Showing 18 changed files with 347 additions and 173 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ on:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
rspec:
runs-on: ubuntu-latest
env:
SLACK_TOKEN: "DUMMY"
steps:
- uses: actions/checkout@v3
- name: Set up Ruby
Expand Down
5 changes: 5 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@ ruby File.read(".ruby-version").rstrip

gem "activesupport"
gem "config"
gem "dotenv"
gem "faraday"
gem "mechanize"
gem "rackup"
gem "roda"
gem "rubyzip"
gem "sidekiq"
gem "slack-ruby-client"

Expand All @@ -21,6 +24,8 @@ group :development do
end

group :development, :test do
gem "hashie"
gem "pry"
gem "rspec", "~> 3.12"
gem "rubocop"
end
7 changes: 7 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ GEM
deep_merge (1.2.2)
diff-lcs (1.5.1)
domain_name (0.6.20240107)
dotenv (3.1.6)
drb (2.2.1)
faraday (2.10.0)
faraday-net_http (>= 2.0, < 3.2)
Expand Down Expand Up @@ -154,6 +155,7 @@ GEM
ruby-progressbar (1.13.0)
rubyntlm (0.6.5)
base64
rubyzip (2.3.2)
securerandom (0.4.0)
shellany (0.0.1)
sidekiq (7.3.6)
Expand Down Expand Up @@ -184,13 +186,18 @@ DEPENDENCIES
activesupport
byebug
config
dotenv
faraday
guard
guard-rspec
hashie
mechanize
pry
rackup
roda
rspec (~> 3.12)
rubocop
rubyzip
sidekiq
slack-ruby-client

Expand Down
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@

<img src="https://github.com/kufu/yokoso/blob/images/sample_animation.gif?raw=true" width="480px">

# 対応入館証について

`yokoso` はビル来客システムの連携させるツールですが、来客システムには以下の2つの仕様が確認されています。

- 2次元バーコードを発行するタイプ
- QRコードを発行するタイプ

本repositoryの所有者であるSmartHRがオフィスをお借りしているビルでは、後者のQRコードを発行するタイプが使用されています。
それに合わせて、mainブランチの仕様もQRコードを発行するタイプの来客システムに対応したものになっています。
また、2次元バーコードを発行するタイプのシステムは利用できなくなったため、そちらの仕様については積極的に保守ができなくなりました。したがって、そちらの仕様については `2D-code` ブランチに切り離しております。
ご利用される環境に合わせてブランチを選定、設定をお願いします。

# 全体像

<img src="https://github.com/kufu/yokoso/blob/images/diagram.png?raw=true" width="640px">
Expand Down Expand Up @@ -86,6 +98,8 @@ heroku local -e .env
- webhook アドレスは `https://{{server_url}}/app/notification`
- サービスは何でも良い
- zapier なら [コチラ](https://zapier.com/app/editor/template/9205?selected_apis=ZapierMailAPI%2CWebHookAPI)
- 先述のQRコードを用いるタイプの来客システムの場合、2次元バーコードのタイプとは異なり入館IDからコードの生成を行えなくなっています(入館IDとQRコードを複合したデータが異なる)
- そのため、添付ファイルをWebhookに含める形でpostするよう設定してください。その時のKeyは `pdf` にしてください
- 参考: <img src="https://github.com/kufu/yokoso/blob/images/zapier.png?raw=true" width="480px">
- Webhook 用メールアドレスは後でサーバサイド環境変数へセットする

Expand Down
36 changes: 21 additions & 15 deletions app/controllers/slack_notification.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,24 @@
require_relative "../models/email"
require_relative "../models/chat_message_sender"
require_relative "../models/admission_code_message"
require_relative "../models/qr_code_pdf"

require "securerandom"

module SlackNotification
def run(request)
mail_body = request["body"]
email = Email.new(mail_body)

# TODO: fix Ruby 3.1+ https://www.rubydoc.info/gems/rubocop/RuboCop/Cop/Security/YAMLLoadQ
messages = open("./config/messages.yml", "r") { |f| YAML.unsafe_load(f) }

res = AdmissionCodeMessage.new(email).post

barcode_url = "https://barcode.tec-it.com/barcode.ashx?data=#{email.recept_id}&code=Code128"
text_guide_jap = messages["notification"]["text_guide_jap"]
text_guide_eng = messages["notification"]["text_guide_eng"]

text_guide_jap.gsub!("RECEPT_ID", "#{email.recept_id}\n#{barcode_url}")
text_guide_eng.gsub!("RECEPT_ID", "#{email.recept_id}\n#{barcode_url}")
text_guide_jap.gsub!("RECEPT_ID", email.recept_id.to_s)
text_guide_eng.gsub!("RECEPT_ID", email.recept_id.to_s)

day_of_the_week_eg2jp = {
"Sun" => "日",
Expand All @@ -36,19 +37,24 @@ def run(request)
text_guide_jap.gsub!("RECEPT_DATE", recept_date_jap)
text_guide_eng.gsub!("RECEPT_DATE", email.recept_date)

ChatMessageSender.new.post_public_message(
icon_emoji: ":office:",
channel: res.channel,
text: "#{text_guide_jap}\n#{text_guide_eng}",
attachments: [
{
title: "バーコード/Barcode",
image_url: barcode_url
}
],
thread_ts: res.ts
zapier_pdf_url = request["pdf"]
file_name_prefix = SecureRandom.alphanumeric(10)
qrcode = QrCodePdf.new(zapier_pdf_url, file_name_prefix)
qrcode.download
qrcode.unzip

ChatMessageSender.new(direct_message_id: res["channel"]).post_admission_badge_message(
{
icon_emoji: ":office:",
channel: email.slack_id,
text: "#{text_guide_jap}\n#{text_guide_eng}",
as_user: true
},
file_paths: qrcode.entry_qr_code_path
)

qrcode.cleanup

""
end

Expand Down
6 changes: 4 additions & 2 deletions app/models/admission_code_message.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ def api_post_body
attachments: [
color: "good",
fields: attachment_fields
] }
],
as_user: true }
end

def api_post_body_direct_message
Expand All @@ -49,7 +50,8 @@ def api_post_body_direct_message
attachments: [
color: "good",
fields: attachment_fields
] }
],
as_user: true }
end

# @return [Array] attachment_field array
Expand Down
58 changes: 57 additions & 1 deletion app/models/chat_message_sender.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
# frozen_string_literal: true

# チャットツールへのメッセージ送信を扱うクラス

require "faraday"
class ChatMessageSender
def initialize
def initialize(direct_message_id: nil)
@slack_api_client = Slack::Web::Client.new(token: ENV.fetch("SLACK_TOKEN"))
@direct_message_id = direct_message_id
end

# @param post_body [Hash]
Expand All @@ -12,4 +15,57 @@ def initialize
def post_public_message(post_body)
@slack_api_client.chat_postMessage(post_body)
end

def post_admission_badge_message(post_body, file_paths: [])
if file_paths.empty?
post_body = {
icon_emoji: ":office:",
channel: @direct_message_id,
text: "入館証の発行に失敗しました",
as_user: true
}
@slack_api_client.chat_postMessage(post_body)

raise "File Path is empty. Check Zapier Settings."
end

files = upload_files(file_paths: file_paths)
posted_message_response = @slack_api_client.chat_postMessage(post_body)
return posted_message_response if file_paths.empty?

@slack_api_client.files_completeUploadExternal(
files: files.to_json,
channel_id: @direct_message_id,
thread_ts: posted_message_response["ts"]
)
end

private

def upload_files(file_paths:)
file_upload_metadata(file_paths: file_paths).map do |metadata|
conn = Faraday.new(url: metadata[:url]) do |f|
f.request :multipart
f.request :url_encoded
f.adapter Faraday.default_adapter
end
payload = { file: Faraday::UploadIO.new(metadata[:file_path], "application/pdf") }
conn.post do |req|
req.body = payload
end

{ id: metadata[:id], title: metadata[:title] }
end
end

def file_upload_metadata(file_paths:)
file_paths.map do |file_path|
res = @slack_api_client.files_getUploadURLExternal(
filename: File.basename(file_path),
length: File.size(file_path)
)

{ id: res.file_id, title: File.basename(file_path), url: res.upload_url, file_path: file_path }
end
end
end
2 changes: 1 addition & 1 deletion app/models/email.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@ def recept_date
end

def recept_id
@params.match(/(?:ID:)(\d+)/)[1]
@params.match(/Guest ID\r\n(\d+)/)[1]
end
end
38 changes: 38 additions & 0 deletions app/models/qr_code_pdf.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# frozen_string_literal: true

require "mechanize"
require "zip"
require "fileutils"

class QrCodePdf
def initialize(pdf_url, prefix)
@pdf_url = pdf_url
@prefix = prefix
@agent = Mechanize.new
end

def download
@agent.get(@pdf_url).save_as("#{@prefix}_qr_code.zip")
end

def unzip
dist_path = "#{@prefix}_qr_code"
Dir.mkdir(dist_path)
Zip::File.open("#{@prefix}_qr_code.zip") do |zip_file|
zip_file.each do |entry|
entry.extract("#{dist_path}/#{entry.name}")
end
end
end

def entry_qr_code_path
qr_code_path = "#{@prefix}_qr_code"

Dir.glob("#{qr_code_path}/*pdf")
end

def cleanup
File.delete("#{@prefix}_qr_code.zip")
FileUtils.rm_rf("#{@prefix}_qr_code")
end
end
7 changes: 5 additions & 2 deletions app/models/slack_message.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,13 @@ def self.post_received_message(modal_submission)
# @see https://github.com/slack-ruby/slack-ruby-client/blob/master/lib/slack/web/api/endpoints/chat.rb
# @private
def received_message_post_body
{ icon_emoji: MESSAGES["interactive"]["icon"],
{
icon_emoji: MESSAGES["interactive"]["icon"],
channel: @modal_submission.slack_user_id,
text:,
attachments: [attachment(fields: received_message_attachment_fields)] }
attachments: [attachment(fields: received_message_attachment_fields)],
as_user: true
}
end

def text
Expand Down
4 changes: 3 additions & 1 deletion app/workers/reception_worker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
require_relative "../models/slack_message"
require_relative "../../config/initializers/sidekiq"

require "dotenv"
Dotenv.load
class ReceptionWorker
include Sidekiq::Worker
sidekiq_options queue: :default, retry: false
Expand Down Expand Up @@ -41,7 +43,7 @@ def perform(slack_modal)
agent.page.form.field_with(name: "mei[]").value = recept_visitor_name.gsub(/[[:punct:]]/, "")
agent.page.form.field_with(name: "kana[]").value = "カナ"
agent.page.form.field_with(name: "mail[]").value = ENV.fetch("MAIL_ADDRESS_WEBHOOK")
agent.page.form.field_with(name: "sinseiemail").value = ENV.fetch("MAIL_ADDRESS_HOST")
agent.page.form.field_with(name: "sinseiemail1").value = ENV.fetch("MAIL_ADDRESS_HOST")
agent.page.form.field_with(name: "sinseitel").value = ENV.fetch("COMPANY_TEL")

# regist
Expand Down
3 changes: 3 additions & 0 deletions config.ru
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ require_relative "app/controllers/slack_slash_command"
require_relative "app/controllers/slack_interactive_message"
require_relative "app/controllers/slack_notification"

require "dotenv"
Dotenv.load

class App < Roda
route do |r|
r.post "app/dialog" do
Expand Down
30 changes: 14 additions & 16 deletions config/messages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,13 @@ notification:
日比谷線・大江戸線「六本木駅」5番出口より徒歩5分
◆ 入館ID
RECEPT_ID
(URLクリックでバーコード画像が表示されます)
RECEPT_ID
◆ ビル入館方法
①(1Fまたは4F)セキュリティゲート前の受付機にバーコードをかざしていただく、もしくは入館IDを入力して下さい。
②受付機より「入館証」が発券されます。
(複数名でご来社の場合は人数分の発券をお願いいたします。同一バーコード/IDをご利用いただけます)
③入館証のQRコードをゲートにかざし入館します。
④ゲート左手15F〜20F用のエレベータにて17Fへお越しください。
⑤SmartHR受付のiPadより担当者をお呼び出しください。
◆ SmartHRオフィスの行き方
https://shanaiho.smarthr.co.jp/n/na0b9906ec236
①添付のPDFのQRコードをゲートにかざし入館します。(複数名でご来社の場合でも、同一QRコードをご利用いただけます。)
②15F〜20F用のエレベータにて17Fへお越しください。
③SmartHR受付のiPadより担当者をお呼び出しください。
※注意点
「入館証」はご退館時まで必要です。大切にお持ち下さい。
```
Expand All @@ -56,11 +50,15 @@ notification:
<Address>
Sumitomo Fudosan Roppongi Grand Tower 17F, 3-2-1 Roppongi, Minato-ku, Tokyo 106-6217
<How to enter>
Please print out your admission tickets at the reception machine on the first or fourth floor, using the ID or barcode below.
You can print out as many tickets as you need with this ID/barcode.
These admission tickets are required to enter and exit the building gates, so please hold on to them.
<Recept ID>
RECEPT_ID
Take the elevator on your left to the 17th floor.
<How to enter>
1. Hold the QR code in the attached PDF over the gate to enter the building. The same QR code can be used even if multiple people are visiting the company.
2. Take the elevator for floors 15-20 to the 17th floor.
3. Call the person in charge using the iPad at the SmartHR reception desk.
Note: You will need your admission pass until you leave the building. Please keep it safe.
```
<<: *common
Loading

0 comments on commit 578c4ef

Please sign in to comment.