- 投稿日:
公式のリファレンスが情報少なすぎるのでメモ
特にこれというフォーマットはなく、ある程度幅広い書式があるようで、公式のドキュメントを見る限り、多少のブレはよしなに解釈してくれるっぽい?
この記事ではoperatorなどの用語が登場しますが、これは私が勝手に名付けたものなので、特に公式の用語ではありません
手組するの面倒なので雑にコマンド生成ツールを作りました。
https://tool.lycolia.info/slack-remider-creator
登録
基本
- フォーマット
/remind [宛先] [メッセージ] [実行日時]
- 例
/remind #random "hoge piyo fuga" at 10:00 every monday, sunday
書式
宛先
宛先 | 意味 |
---|---|
me | 自分 |
@someone | メンション |
#channel | チャンネル |
メッセージ
"hoge piyo fuga"
のように書けるが、hoge piyo fuga
でも一応通じる
ダブルクォートで囲む場合、改行も利用可能
実行日時
<oparator> <repeat-operator>
<oparator>
で実行日時を設定<repeat-operator>
で繰り返しを設定
- 例
at 10:00 every monday, sunday
oparator
オペレーター | 例 | 意味 |
---|---|---|
in <value> (seconds \ | minutes \ | hours) |
at <HH:mm> | at 1700 | 指定時刻に実行 |
on <yyyy-MM-dd HH:mm> | on 2023-10-10 09:15 | 指定日時に実行 |
repeat-operator
every <operator>
- 例
every monday, sunday
operator |
---|
day |
weekday |
sunday, monday, tuesday, wednesday, thursday, friday, saturday |
削除
/remind list
でリスト表示して消す
一覧
/remind list
- 投稿日:
確定申告後に国税納付する方法の備忘録
前提条件
- やよいの○色申告でe-Taxへの送信まで行っている
- 白でも青でもどっちでもOK
- e-Taxの利用者登録をしている
手順
- e-Tax のログインページを開く
- e-Taxソフト(WEB版)へログインする
- 「送信結果・お知らせ」から「納付情報登録依頼」を開く
- 納付方法が列挙されたページが開くので好きな納付方法を選ぶ
- コンビニ納付が一番楽
- この場合QRコードはPDFに出してスマホで出せるようにすると良い(スクショは読み取れないことがある
- 投稿日:
Twilogには特定ツイートを一括で削除する機能がなかったので適当に作りました。
ページネーション機能はないので暇な人作ってください。
事前にページネーションリンクを取得して各ページのHTMLを取得してID引っこ抜けばできると思います。多分
/**
* これは何?
* Twilogで現在表示中のページのツイートをすべて削除します
* 事前に削除対象のツイートを日付選択や検索などで表示した上で実行します
* ページネーション機能はありません
*
* 使い方
* このスクリプトをChromeのDevtoolsのConsoleにコピペして実行します
* 複数ページある場合はページをリロードして同様に操作します
*/
const getSession = () => {
const scr = [...document.getElementsByTagName('script')].find(v => v.text.match(/j-delete-tweet.rb/) !== null)
const mat = scr.text.match(/j-delete-tweet.rb\?c=(.+?)&.+&token=(.+)"/)
return {
c: mat[1],
token: mat[2]
}
}
const getDeleteIdList = () => {
return [...document.getElementsByClassName('tl-del')]
.map((el) => {
const mat = el.children[0].onclick.toString().match(/delete_tweet\('(.+?)','(.+?)',/)
return {
date: mat[1],
status_id: mat[2]
}
}
)
}
const createDeleteRequest = (c, token) => {
return async (date, statusId) => {
return fetch(`https://twilog.org/j-delete-tweet.rb?c=${c}&date=${date}&status_id=${statusId}&token=${token}`)
}
}
const session = getSession()
const requestDelete = createDeleteRequest(session.c, session.token)
const deleteIdList = getDeleteIdList()
let index = 0;
setInterval(async () => {
if (index < deleteIdList.length) {
const del = deleteIdList[index]
const result = await requestDelete(del.date, del.status_id)
console.log(`${index} / ${deleteIdList.length}`, result)
++index
}
}, 1000)
- 投稿日:
IPv6のOCNバーチャルコネクト環境ではwell-known portsが利用出来ずWebサーバーを公開するのに支障がありますが、今回はそれを乗り越えるための手法を紹介します。
自宅サーバーにhttps://service.example.com/
のようにポート指定なしのサブドメインでアクセスできるようにするのがゴールです。
構成
自宅サーバーの手前にCDNを挟み、CDNを経由して接続させるようにします。要するに手前にリバプロを生やしておくわけです。
前提
- Google Domainsを利用している
- ルートドメインを保有している
- AWSのアカウントがある
前準備
DNS レコードに次のドメインを作っておく
用途 | ドメイン | レコード | データ |
---|---|---|---|
CDN 用のドメイン | cdn.example.com | A | サーバーの IP |
公開用のドメイン | service.example.com | A | サーバーの IP |
自宅サーバーを公開可能な状態にする
叩き台程度ならserveを使うのが手っ取り早いですが、Node.jsでサーバー立てるのもありだと思います。今回は叩きなのでHTTPにしていますが本番運用するときはHTTPSにしましょう。
以下サンプル
const http = require('http');
const port = 12345;
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello world!');
console.log(req.headers.host, req.socket.remoteAddress);
});
server.listen(port, () => {
console.log(`Running at http://localhost:${port}`);
});
手順
- CloudFrontを開く
- 「ディストリビューションを作成」
- 「オリジンドメイン」に
cdn.example.com
を設定 - 「プロトコル」を選ぶ
cdn.example.com
の「ポート」を設定する- 設定までスクロール
- 「代替ドメイン名 (CNAME) - オプション」で「項目を追加」し
service.example.com
を入力 - 「カスタムSSL証明書 - オプション」で「証明書をリクエスト」
- 「パブリック証明書をリクエスト」
- 「完全修飾ドメイン名」に
service.example.com
を入力し「リクエスト」 - 「証明書を表示」
- DNSレコードに「CNAME名」で「CNAME値」を追加
- 「保留中の検証」が終わるのを待つ
- 「カスタムSSL証明書 - オプション」で作成した「ACM証明書」を選択
- 「ディストリビューションを作成」
service.example.com
のDNSレコードをCNAMEにし、データを「ディストリビューションドメイン名」に変更https://service.example.com/
にアクセスできればOK
参考資料
トラブルシュート
ディストリビューションを削除したい
- ディストリビューションの一覧で消したいのにチェック入れて無効化
- しばらく待つ
- 消したいのにチェック入れて削除
後書き
副次的効果ですがCDN挟んでキャッシュされてるお陰で連続アクセスしてもサーバーまでリクエスト来ないので感動しました。
CloudFrontには他にも様々な機能があるみたいなので活用できれば便利そうです。
- 投稿日:
割とハマってだるいので今回はサンプル程度にPRの一覧を取得して個別にSlackに投げるものを作ってみます
API トークンの入手
まずはSlack APIを叩くためのトークンをゲットします
- Create an appからアプリを作成
- 左のメニューからFeatures -> OAuth & Permissions
- Scopesを設定
- 今回はBot Token Scopesを
chat:write
とします
- 今回はBot Token Scopesを
- 左のメニューからSettings -> Install App to Your Teamでアプリをインストール
- トークンが吐き出されるのでメモする
GitHub Actions Workflowsの作成
SECRETの設定
- Slack APIトークンをリポジトリのSecretsに突っ込んでおきます
- 名前は一旦
SLACK_TOKEN
とします
Workflowsの作成
前提
- PR一覧の取得には actions/github-script を利用します
- GitHub内部の情報を抜いたり、JSで処理を組みたいときに重宝します
- APIリファレンスが読みやすいので、使うのにはあんま苦労しないと思います
- Slack APIを叩くのにはcurlを利用します
- actions/github-scriptから叩くのは多分難しいです
ベースの作成
これに肉付けをしていきます
name: Post to slack example
on:
workflow_dispatch:
jobs:
post-slack:
runs-on: ubuntu-latest
steps:
PR一覧の取得
List pull requestsにある通りに進めていきます
- uses: actions/github-script@v6
id: set-result
with:
result-encoding: string
script: |
const { data: respPulls } = await github.rest.pulls.list({
owner: context.repo.owner,
repo: context.repo.repo,
});
console.log(respPulls);
PR一覧の加工
こんなデータを取る感じで組んでいきます
API仕様は List pull requests を参照
type PullRequest = {
id: number;
reviewers: string[];
};
先ほど取得したrespPulls
を上記の型付けになるように加工します
const getReviewersName = (requested_reviewers) => {
return requested_reviewers.map((reviewers) => {
return reviewers.login;
});
};
const getPullRequests = (pulls) => {
return pulls.map((pull) => {
return {
id: pull.number,
reviewers: getReviewersName(pull.requested_reviewers),
};
});
};
const pulls = getPullRequests(respPulls);
Slackに投げるメッセージの作成
こんなメッセージをPRの数分組んでいきます
なお実際にSlackでメンションを作る場合はGitHubのスクリーン名とSlackのユーザー IDの突き合わせ処理が別途必要です。やり方は別途後述します
@foo @bar
https://example.com/pulls/1
やっていること
- 上記のフォーマットでメッセージを作成
- シェルスクリプトで配列として扱うためにBase64にエンコード
- 改行コードが混ざっていると扱いづらいので
- エンコードした文字列をスペース区切り文字列として連結
AAA BBB CCC ...
みたいな
- 最後にWorkflowsの戻り値として設定しています
const encodedMessages = pulls.reduce((messages, pull) => {
const reviewersBuff = pull.reviewers
.reduce((acc, cur) => {
return `${acc}${cur} `;
}, '')
.replace(/ $/, '');
const reviewers = reviewersBuff === '' ? 'レビュアー未設定' : reviewersBuff;
const message = `${reviewers}\\nhttps://example.com/pulls/${pull.id}`;
const encodedMessage = Buffer.from(message).toString('Base64');
return `${messages}${encodedMessage} `;
}, '');
return encodedMessages;
curlを利用してSlack APIを叩く
やっていること
encodedMessages=(${{steps.set-result.outputs.result}})
- 前項で作った文字列を配列として取得しています
for message in ${encodedMessages[@]}
- foreach的なやつです
- 改行コードがこの時点で存在すると上手くいきません
decoded_mes=$(echo ${message} | base64 -di)
- ここでBase64エンコードをデコードします
postSlack "$decoded_mes"
- 別引数にならないように
""
で固めます
- 別引数にならないように
- curl叩いてるところ
-d
の中をヒアドキュメントで展開するのが味噌です- 単純に文字列として扱うと変数展開が起きてJSONが壊れます
- run: |
postSlack() {
local mes=$1
curl -sS https://slack.com/api/chat.postMessage \
-H 'Authorization: Bearer ${{ secrets.SLACK_TOKEN }}' \
-H 'Content-Type: application/json; charset=UTF-8' \
-d @- <<EOF
{
token: "${{ secrets.SLACK_TOKEN }}",
channel: "#api-test",
text: "$mes"
}
EOF
}
encodedMessages=(${{steps.set-result.outputs.result}})
for message in ${encodedMessages[@]}
do
decoded_mes=$(echo ${message} | base64 -di)
postSlack "$decoded_mes"
done
コード全体
name: Post to slack example
on:
workflow_dispatch:
jobs:
post-slack:
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v6
id: set-result
with:
result-encoding: string
script: |
const getReviewersName = (requested_reviewers) => {
return requested_reviewers.map((reviewers) => {
return reviewers.login;
});
};
const getPullRequests = (pulls) => {
return pulls.map((pull) => {
return {
id: pull.number,
reviewers: getReviewersName(pull.requested_reviewers),
}
});
}
const { data: respPulls } = await github.rest.pulls.list({
owner: context.repo.owner,
repo: context.repo.repo,
});
const pulls = getPullRequests(respPulls);
const encodedMessages = pulls.reduce((messages, pull) => {
const reviewersBuff = pull.reviewers.reduce((acc, cur) => {
return `${acc}${cur} `
}, '').replace(/ $/, '');
const reviewers = reviewersBuff === '' ? 'レビュアー未設定' : reviewersBuff;
const message = `${reviewers}\\nhttps://example.com/pulls/${pull.id}`;
const encodedMessage = Buffer.from(message).toString('Base64');
return `${messages}${encodedMessage} `
}, '');
return encodedMessages;
- run: |
postSlack() {
local mes=$1
curl -sS https://slack.com/api/chat.postMessage \
-H 'Authorization: Bearer ${{ secrets.SLACK_TOKEN }}' \
-H 'Content-Type: application/json; charset=UTF-8' \
-d @- <<EOF
{
token: "${{ secrets.SLACK_TOKEN }}",
channel: "#api-test",
text: "$mes"
}
EOF
}
encodedMessages=(${{steps.set-result.outputs.result}})
for message in ${encodedMessages[@]}
do
decoded_mes=$(echo ${message} | base64 -di)
postSlack "$decoded_mes"
done
Appendix:Slackにメンションを投げる方法
Slackへ実際にメンションを投げるのはユーザーIDを指定する必要があります
参考:Formatting text for app surfaces
"text": "<@U024BE7LH> Hello"
のようにすることでメンションを投げられます
ユーザーIDはSlackアプリから相手のプロフィールを開き、そこにあるハンバーガーメニューみたいなやつから取れます。一応取得用のAPIもあります