はじめに
「フルリモートワーク」と聞くと、効率的な働き方を想像する人も多いでしょう。しかし、実際にはリモート特有の課題がつきものです。特に「電話対応」というアナログな部分では、情報共有の手間がかかりがちです。この記事では、そんな課題を楽しく解決した私の体験をご紹介します。
ハウインターナショナルのリモートワーク環境
私が所属するハウインターナショナルは、フルリモートワークを実現している会社です。本社は福岡県飯塚市にありますが、東京、愛知、沖縄、さらには「闘魂の国」からもメンバーが働いています。この分散したチーム環境において、電話対応をどのように効率化するかが大きな課題でした。
電話対応の流れは次のようなものでした:
- 電話代行サービスが受けた電話の内容(誰から誰への電話だったのか)をメールで送信
- メールを受け取った担当者が内容を確認
- Slackで該当者にメンションを付けて連絡
このプロセスはシンプルに見えますが、手動の手間が課題でした。
解決策:自動化への挑戦
私はこの手動プロセスの「2番と3番」を自動化することに挑戦しました。その結果、新しいフローは次のように改善されました:
- 電話代行サービスが電話の内容をメールで送信
- ボットが、そのメールを解析し、Slackで該当する担当者にメンションを付けて電話があったことを自動通知
たったこれだけの改善ですが、業務効率は格段に向上しました。
担当者は、リンボーダンスをはじめました!
メール処理の奥深さ
自動化の鍵となるのは「メールの受信処理」です。一見簡単そうに思えるこの部分が、実は奥深いものでした。
Gmail APIを活用
ハウインターナショナルは、Google Workspaceを利用しているのでGmail APIを使いました。
Rubyの心得があるので、Rubyを使いました。
以前は、 https://developers.google.com/gmail/api/quickstart/ruby というページがありましたが、2024-12-11に見たらありませんでした!
google-api-ruby-client Gem自体は、開発が続けられているようです。
以下は簡単なコード例です:
Gemfile
# frozen_string_literal: true
source "https://rubygems.org"
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
# gem "rails"
gem "google-api-client"
私はすごく古いものを使っちまっているようです。以下の記事を参考にアップグレードするとよいと思います。
lib/awesome/gmail.rb
Gmailを受信する処理です。
require "google/apis/gmail_v1"
require "googleauth"
require "googleauth/stores/file_token_store"
require "fileutils"
OOB_URI = "urn:ietf:wg:oauth:2.0:oob".freeze
APPLICATION_NAME = "Gmail API Ruby Quickstart".freeze
CREDENTIALS_PATH = "credentials.json".freeze
TOKEN_PATH = "token.yaml".freeze
SCOPE = Google::Apis::GmailV1::AUTH_GMAIL_READONLY
MAX_RESULTS = 20
module Awesome
class Gmail
class << self
def authorize
client_id = Google::Auth::ClientId.from_file CREDENTIALS_PATH
token_store = Google::Auth::Stores::FileTokenStore.new file: TOKEN_PATH
authorizer = Google::Auth::UserAuthorizer.new client_id, SCOPE, token_store
user_id = "default"
credentials = authorizer.get_credentials user_id
if credentials.nil?
url = authorizer.get_authorization_url base_url: OOB_URI
puts "Open the following URL in the browser and enter the " \
"resulting code after authorization:\n" + url
code = gets
credentials = authorizer.get_and_store_credentials_from_code(
user_id: user_id, code: code, base_url: OOB_URI
)
end
credentials
end
def list
gmail = Google::Apis::GmailV1::GmailService.new
gmail.authorization = authorize
messages = []
next_page = nil
begin
result = gmail.list_user_messages('me', max_results: MAX_RESULTS, page_token: next_page)
messages += result.messages
break if messages.size >= MAX_RESULTS
next_page = result.next_page_token
end while next_page
messages.map(&:id)
end
def get(id)
gmail = Google::Apis::GmailV1::GmailService.new
gmail.authorization = authorize
result = gmail.get_user_message('me', id)
payload = result.payload
headers = payload.headers
date = headers.any? { |h| h.name == 'Date' } ? headers.find { |h| h.name == 'Date' }.value : ''
from = headers.any? { |h| h.name == 'From' } ? headers.find { |h| h.name == 'From' }.value : ''
to = headers.any? { |h| h.name == 'To' } ? headers.find { |h| h.name == 'To' }.value : ''
subject = headers.any? { |h| h.name == 'Subject' } ? headers.find { |h| h.name == 'Subject' }.value : ''
body = payload.body.data
if body.nil? && payload.parts && payload.parts.any?
body = payload.parts.map { |part| part.body.data }.join
end
{id: result.id, date: date, from: from, to: to, subject: subject, body: body}
end
end
end
end
このコードにより、Gmailのメッセージを簡単に取得できます。
lib/awesome/slack.rb
メール解析後、Slackに通知を送る仕組みを構築しました。このプロセスではIncoming Webhook(非推奨)を活用しています:
require 'net/http'
require 'uri'
require 'json'
URL = ENV['GMAIL_TO_SLACK_WEB_HOOK_URL']
CHANNEL = ENV['GMAIL_TO_SLACK_CHANNEL']
module Awesome
class Slack
class << self
def post(text)
uri = URI.parse(URL)
https = Net::HTTP.new(uri.host, uri.port)
https.use_ssl = true
req = Net::HTTP::Post.new(uri.request_uri)
req['Content-Type'] = 'application/json'
payload = {
text: text,
channel: CHANNEL,
username: 'awesome_bot',
icon_emoji: ':telephone:',
link_names: 1
}.to_json
req.body = payload
res = https.request(req)
end
end
end
end
これにより、電話の情報がSlackで即座に通知されるようになりました。開発中はSlackの通知が来るたびに、成功を実感し楽しい気持ちになりました。
lib/main.rb
司令塔となるプログラムです。
1分ごとに実行しています。だって、電話は常にかかってきますし、その用件はいつも急用ですので。
ボットは疲れを知りません。どんどん働いてもらいましょう。
require 'awesome/gmail'
require 'awesome/slack'
require 'time'
require 'csv'
require 'set'
LAST_AT = 'last_at'
FROM = ENV['GMAIL_TO_SLACK_FROM']
if File.exist?(LAST_AT)
last_at = Time.parse(open(LAST_AT, &:read))
else
last_at = Time.now - 60 * 5
end
ids = Awesome::Gmail.list()
filtered = ids.map { |id| Awesome::Gmail.get(id) }.filter { |mail| !mail[:date].empty? && (Time.parse(mail[:date]) > last_at) }
.filter{ |mail| mail[:from].include?(FROM) }
names = {}
CSV.foreach('users.csv') do |row|
names[row[0]] = row[1]
end
NAMES = names
filtered.each do |mail|
body = mail[:body].force_encoding('UTF-8')
usernames = Set.new
NAMES.each do |key, username|
usernames << username if body.include?(key)
end
text = usernames.to_a.join(' ') + "\n" + body
Awesome::Slack.post(text)
end
last_mail = filtered.sort_by{ |mail| Time.parse(mail[:date]) }.last
if last_mail
open(LAST_AT, "w") { |f| f << last_mail[:date] }
end
本文に該当者の名前が含まれていたら、メンションを本文の先頭に挿し込むことをしています。
ちなみに、users.csv
はこんな感じ(寛至)のCSVファイルです。
山内,@yamauchi
やまうち,@yamauchi
ヤマウチ,@yamauchi
闘魂,@inoki
猪木,@inoki
いのき,@inoki
イノキ,@inoki
どんなに優れたことをしたのか
大仰かもしれませんが語らせてもらいます。
このプロジェクトは、以下の点で優れています:
- 業務効率化:電話対応のプロセスを大幅に簡略化(担当者がメールを監視していた負担を減らし、他のクリエイティブな仕事に時間を割り当てることができます)
- 開発の楽しさ:メール解析やSlack通知というリアルタイム性のある機能の構築
- チームへの影響:手作業の負担を減らし、リモートワークの環境をさらに最適化
担当者は、優雅にバレエをはじめました!
楽しさと達成感
開発を通じて、単なる業務効率化以上の楽しさを感じることができました。メンバーから「通知が便利になった」と感謝の声をもらえたときの喜びはひとしおです。
まとめ
電話対応という一見地味な部分でも、自動化と技術の力で大きな変化をもたらすことができます。この体験を通じて、(おこがましいかもしれませんが)WHI様のミッションである「『はたらく』を楽しくする」精神を形にできたと感じています。
ぜひ皆さんも、身の回りの課題を楽しく解決する方法を見つけてみてください!