Casser Notes

SASメインで使っていたエンジニアのステップアップの記録

Slackを見ない人にもLINEでSlackの更新を伝えたいこの気持ちをカタチに

何?

とあるSlackで告知をした時に、普段Slackを見ない人にもお知らせがあったことをお知らせしたかった、お知らせのお知らせ(?)

どうする

LINEなら見るやろという発想

お金

お金あるある「ない」

どうしよ

GASとAPIで何とかなるやろの精神

構成

シンプル(に見えた)

最終的な構成

Slack Channel → Event Subscriptions → Google Apps Script → LINE Messaging API → LINEグループ

使ったもの

  • Google Apps Script(Webhookの受け口)
  • Slack Event Subscriptions API
  • LINE Messaging API

なんとかなるやろと思っていたら意外とハマった

ハマり1 : 古いAPI使ってた

Slackと連携する方法を調べてみると、「Outgoing Webhooks」というのが簡単そうだったので、まずはこれで実装してみた

が、Outgoing WebhooksはLegacy機能で、今はそもそも設定する方法がな買った

どうする

別件でSlackの新着チャンネルを検知する機構を作った時に使ってた「Event Subscriptions」を使うことにした

最初の Verify

// 結局こんな感じに書き直し
function doPost(e) {
  const data = JSON.parse(e.postData.contents);

  // Slackがちゃんとしたエンドポイントか確認してくる
  if (data.type === 'url_verification') {
    return ContentService.createTextOutput(data.challenge);
  }

  // 実際のメッセージ処理
  if (data.type === 'event_callback') {
    return handleEventCallback(data);
  }
}

ハマリ2: Slack App の OAuth 権限

動くようになって喜んでたらユーザー名が取れてなかった (IDで表示される) ログを見ると↓

Slack API エラー: missing_scope
ユーザー情報取得開始: U08PV8P921F

最初は最低限の権限しか設定してなかったから不足してた

Bot Token Scopes:
- channels:history  // メッセージ読むだけ

ユーザー名も表示したかったので、権限を追加

Bot Token Scopes:
- channels:history
- users:read  // これ追加

権限を追加したら、Slack Appの再インストールが必要だった

ハマり23: 同じメッセージが何度も送られる

動かしてみたら、なんか何回も同じSlackメッセージがLINEに複数回送信される 困った

挙動を見る限り、Slackが同じイベントを複数回送信することがあるらしく、それに対処する必要があった (やめてくれ〜)

// 重複チェック機能を追加
function isEventProcessed(eventId) {
  const properties = PropertiesService.getScriptProperties();
  const processedEvents = properties.getProperty('PROCESSED_EVENTS') || '';

  return processedEvents.includes(eventId);
}

function markEventAsProcessed(eventId) {
  const properties = PropertiesService.getScriptProperties();
  const processedEvents = properties.getProperty('PROCESSED_EVENTS') || '';
  const updatedEvents = `${processedEvents},${eventId}`;

  // 直近1000件だけ保持(容量制限があるので)
  const eventArray = updatedEvents.split(',').slice(-1000);
  properties.setProperty('PROCESSED_EVENTS', eventArray.join(','));
}

Slackから送られてくるイベントには一意のIDが付いていので、それを使って「すでに処理済みかどうか」をチェックするようにした DBないし、スプレッドシート作るのもめんどいなと思ったけど、スクリプトプロパティで保持すればいいという発想に (Claudeが)

容量制限があるので、古い履歴は削除するようにしてる

これで重複送信は解決した

ログ

ログがあんまり出てなかった 最初はあまり深く考えずに実装してたので、何か問題が起きてもどこで失敗してるのかよくわかラズ

GASの実行ログ、イベントを受けて動く系だと役に立たないんですよね

なのでログを出してCloud Logs (だっけ)で見れるように

// 結局こんな感じで詳細なログを出すようにした
function sendToLine(message, eventId) {
  console.log(`🚀 LINE送信開始 - EventID: ${eventId}`);
  console.log(`📝 送信メッセージ長: ${message.length}文字`);

  try {
    const response = UrlFetchApp.fetch(LINE_API_URL, options);
    console.log(`✅ LINE API レスポンス: ${response.getResponseCode()}`);

    if (response.getResponseCode() === 200) {
      markEventAsProcessed(eventId);
      console.log(`🎯 イベント処理完了: ${eventId}`);
    }
  } catch (error) {
    console.error(`❌ LINE API エラー:`, error.toString());
    console.error(`エラー詳細 - Message: ${error.message}, Stack: ${error.stack}`);
  }
}

各処理にEventIDを付けて追跡できるようにしたのも良かった、たどりやすい

作ってみて気づいた便利な機能

テスト用関数を作っておくと楽

開発中は何度もテストすることになるので、専用の関数を作っておくと便利だった

// 手動でLINE送信テストする用
function testLineMessage() {
  const testMessage = "🧪 テストメッセージ\n" + new Date().toLocaleString('ja-JP');
  sendToLine(testMessage, `test_${Date.now()}`);
}

// メンション検出がちゃんと動くかテスト
function testChannelMentionDetection() {
  const testText = "@channel テスト投稿です";
  const result = containsChannelMention(testText);
  console.log(`メンション検出結果: ${result}`);
}

エラーが起きても止まらないようにしておく

LINE APIが一時的にダウンしたりすることもあるので、エラーハンドリングは一応

function robustApiCall(apiFunction, fallbackMessage = "システムエラーが発生しました") {
  try {
    return apiFunction();
  } catch (error) {
    console.error(`API呼び出し失敗: ${error}`);
    return { success: false, error: fallbackMessage };
  }
}

設定忘れチェック

function validateConfiguration() {
  const required = ['LINE_CHANNEL_ACCESS_TOKEN', 'LINE_GROUP_ID', 'SLACK_BOT_TOKEN'];
  const properties = PropertiesService.getScriptProperties();

  const missing = required.filter(key => !properties.getProperty(key));

  if (missing.length > 0) {
    throw new Error(`設定不足: ${missing.join(', ')}`);
  }

  console.log('✅ 設定確認完了');
}

振り返り

まあ、ほとんどClaude Codeにやらせたんですが、意外と時間かかった LINE APIを使うのは初めてだったので楽しかった

今後やりたいことメモ

# これは何か

CREを1年ほどやった感想と共に今後の自分のやりたいことを考えてみた

 

# 今この記事を書いてる時何してる?

電車に乗ってiPhoneのブックアプリで本を読んでる

図書館でこの本を借りて読んだところ、不思議の読書がスイスイ進むようになり、積み本の解消が捗ってる

 

 

# 何読んでるの

現在所属している企業に、月に1万円まで技術書を買っても良いと言う制度がある

この制度で買った本を積んでしまっていたので読んでる

 

主にオライリー社の本

 

# で本題

この記事を書く前は1年ぐらい前に買ったDBREの本を読んでた

 

 

元々SASやDB周りで仕事をしていたのでこの辺の話は愛着がある

 

CREを1年やった感想は、「この世界に出てきてよかったな」

というものと

「一周目でやらない方が良いな」

ということ

 

## どういうこと?

DBRE本のほかにdevopsやアジャイル、マイクロサービスの本も読んでいる

 

 

CREは、この辺りを経験してスキルのついた「2周目」の人がやった方が価値発揮が出来そう

 

自分のようなオンプレ専門のSIer経験者がモダンなWebサービス環境に馴染む一歩目として、

のCREもありかなと思っていたがちょっと違うかもしれない

 

もちろん、自分のスキルはついてきてるし社の役に立ってはいるつもりだけど、マンパワーで推しているだけで技術のレバレッジが効いてない

 

"Customer Reliability Engineering" という名前のロールであり、力押しするべきではないなと

 

CREをやりながら技術を学んでいくのもいいが、少なくともチーム内に1人は2周目の人がいると突破口が生まれて良さそう

 

今後は自分が2周目人材になってこれからくる人たちの育成に尽力するつもりだが、

その前に一度開発経験を挟むべきだったなという反省

 

 

# で今後は

すっかりお気持ちポエムになってしまった

 

CREの仕組み、組織をある程度軌道に乗せたら、

もう一度システムと向き合いたい

 

あたりか

 

そこに到達するために、まずは今の持ち場を精一杯やっていく

 

そんなことを思う土曜日



 

Macのマイク入力音量が勝手に変わるのでその場しのぎの対処をした

macにBlue Yetiをつないで使っているが、 いつの間にか勝手に音量が小さくなっていて、Meetの会議で声が聞こえなくなることがあった

Meetはノイズキャンセリングがリリースされたので、それにかき消されて声聞こえないよーと言われることもあった


調べてみるとChromeの機能で勝手に調整されるということが書かれていた

qiita.com

qiita.com

自分の環境だと会社端末、個人端末ともChromeを起動していない時も勝手に調整されるようだったのと無闇に拡張機能入れられない事情もあり、、

こちらの記事を拝見するとosscriptでボリュームを変更できるらしい

akkeshilog.blogspot.com

ということでとりあえずシェルスクリプトの無限ループで回避してみる

while :; do
    osascript -e "set Volume input volume 50"
    sleep 5
done

デバッグ的に見たいときは osascript -e "get volume settings" で表示させれば良い

while :; do
    osascript -e "set Volume input volume 50"
    # ↓ デバッグ的にコンソールに出力したい場合はコメント外す
    osascript -e "get volume settings"
    sleep 5
done

とりあえず叩く

% zsh ./main.sh
output volume:33, input volume:53, alert volume:98, output muted:false
output volume:33, input volume:53, alert volume:98, output muted:false

これが f:id:Casseroles:20220409144535p:plain ↓ こう f:id:Casseroles:20220409144402p:plain

なぜ50 でなく53にるのか不明だけどとりあえず目的は達成したのでヨシ!

ミーティング前など下がると困る時に叩くようにしよう


osscriptの存在を初めて知ったけど結構便利に使えるのかな

build command-line-arguments: cannot find module for path と怒られた

GOPATH前提のハンズオンをやっててつまづいたところメモ

build command-line-arguments: cannot find module for path {{path/to/golang/dir}}

goのバージョンは go 1.17

import "./fuga"

と書いて go fmt hoge.go なり go run hoge.go をすると

build command-line-arguments: cannot find module for path {{path/to/golang/dir}}

と怒られた

結論は、 GO11MODULE=on なのに早退パスを使ってたのが原因

ディレクトリが

hoge
   |
   ーfuga.go
hoge.go

な感じだったので、hogeディレクトリで

% go mod init hoge

とした上で

import "hoge/fuga"

と書いたら解決した

ちなみに、最初はhoge/fugaディレクトリにも go.modを置いてたので

main.go:5:2: package hoge/fuga is not in GOROOT (/path/to/hoge/fuga)

な感じで怒られた

こちらを参照し、hogeだけにgo.modを置いたら解決した

qiita.com

EC2のAmazonLinux2にMySQLをインストールするところで詰まった

ご無沙汰しています

諸般の事情によりSASをやっていた会社からSaaS企業に身を移しております そのブログはゆくゆくは書くとして…

今回はAWSのハンズオンで詰まったことを備忘録的に残しておきます

今の職場ではAWSをメインに使っており、業務をする上でAWSを一定知っておいた方が良かろうということで AWS Certified Solutions Architect – Associate の勉強をしています

まずはUdemyで評判がいいらしいこちらの教材をハンズオンとしてやっています

www.udemy.com

この中でEC2のインスタンスMySQLを導入するという項目があるのですが、 前職ではWindowsServerでしか作業しなかったのでLinuxにまったく疎く…

使ったAMIは標準のこちら Amazon Linux 2 AMI (HVM) - Kernel 5.10, SSD Volume Type - ami-0404778e217f54308 (64 ビット x86) / ami-03195e1b4a3b0b993 (64 ビット Arm)

yum localinstall https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm -y`

yum install mysql-community-server -y 

でインストールしようとしましたが依存関係のあるモジュールか何かのエラーが出てしまい…(手元にログ残してなかった) 結論はこちらのブログのお陰でなんとか手順を進めることができました

hit.hateblo.jp

この教材当時だとMySQL8が出てなかったとかあるのかもしれませんが、 mysqld ではなく mysqld.service と指定が必要とか、 MySQL内部でも ALTER USER 'root'@'localhost' IDENTIFIED BY '新パスワード'; のようにユーザ名、ホスト名をシングルクオートで囲わないと ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near のような エラーが出たり…

いろいろ苦しめられました

しかし、前職だと物理サーバを事前に見積して搬入してOracleのインストールに何日もかけて…とやっていたので、 一瞬でサーバーが経って簡単にDBも立てられて、(Community Editionと商用機の違いだったりはありますが)ハンズオンしながらも隔世の感があります

今後もこんな感じでメモ程度にブログ更新できたり、自分のプロフィールサイト程度のものを建てられればと思ってます

最近はSASを全然触ってないので恋しくなる時がありますが、あれはあれでいいもんですよね 今でも使いたくなる時があります

それではまた

保守契約と開発契約

昨日は以前よりくすぶっていた問題の対応をした

 

今は以前開発したシステムの追加開発をしているのだけど、その契約の中で以前開発した部分の本番発生事象について、多くの問い合わせを受けている

 

ウチは1次請けではないのだけど、↑についてお客様は協力して欲しい、汲み取って欲しい、

といいつつ、1次請けのベンダーさんは開発契約なのでそこは対応できず、別途保守契約が必要、と仰っている

 

自分はその間に挟まれる立場なのだけど、これもまた難しい

 

まず、実情としては現行システムの問い合わせ工数は積んでないのは事実で、そこの対応で開発が遅れ始めてるのも事実

 

一方、これはやりません、と言われるとむかっとする、

実態としてそこが分からないと使えないよ、というお客様の言い分も、分からないでもない

 

ただ、やっぱりビジネスとしてやってるので、自分たちが動くのにはお金がかかる

 

 

前に保守していたお客様のところでも思ったけど、SIerは比較的言われたことをはいはい聞いてしまう(自分も)

一方、自社製品を持つベンダーさんや1次請けの会社さんはそこの仕切りが厳しかったりする

 

 

雰囲気で喋ると、海外は後者で、あるべきも後者だとは思う

 

ただ、今のお客様のマインドにそれをぶつけると喧嘩になるので、徐々に移行して理解を得るしかないのかなーと思うこの頃でした

 

これを書いて、ちゃんと顧客を叱るのが良いベンダーみたいな記事を思い出した(探しても見つからなかった…)

 

我々で、お客様を育成していきたい、お客様が自走できるように

 

昨日も楽しかった

 

忘年LT大会

昨日は「エンジニアの登壇を応援する会」さんの エンジニアの成長を応援する忘年LT大会2019 に参加してきました

 

エンジニアの成長を応援する忘年LT大会2019 - connpass

 

正直、外部イベント初参加の自分からしたら皆さん活発でコミュ障を発揮しまくりでしたが、

LT会ってこういうものなんだ…を知れてよかったです

 

また、主催のありあきさんからも

というコメントを頂けて感謝です

 

 

まだまだ自社内の殻にこもっていますが、外の世界に踏み出せるよう、積極的にイベントに参加していきます…

 

とりあえず、どこにも見せないけど自分が今年やってきたことをまとめてみた、のスライドでもつくってみようかな