この記事は、ConoHa Advent Calendar 2024の10日目の記事です!
昨日 (9日) の記事は @honocaさんの「このはぴば2024 登壇した資料」です。
ご無沙汰しております。今年も懲りずに誕生日枠をとりました、鮎月(あゆつき)でございます。
今回は、このはちゃんをメイドさんにします!
やっぱりおうちにメイドさん欲しいですよね!?!? このはちゃんにメイドさんになってもらいましょう!
ちなみにこれを書いているのは12月9日です。間に合うのでしょうか。
何をしてくれるメイドさんがほしいのか
最近寒いですよね。私みたいな一人暮らしでは、家に帰っても誰もいません。いたら警察呼びます。この時期は、真っ暗でただ寒い部屋に帰って寝るだけ。虚無です。なんという虚無なのでしょう。
では、何をしてくれるメイドさんがいたら嬉しいでしょうか?
- 家に帰るとおかえりと言ってくれて
- 暖かくて明るい部屋が待っている
では2を達成できるメイドさんにしましょう!!
……まぁ、1も欲しいですけどね。今回は諸事情(時間)でカットします。
用意するもの
以下の通りです
- このはちゃん
無理ですね。以下の方法で代用しましょう。
- ConoHa VPSのインスタンス
- SwitchBot ハブミニ
暖かくて明るい部屋が欲しいので、遠隔でリモコン操作をできるSwitchBot ハブミニを用意します。
話が少しずれますが、皆さんハブミニの電源どこから取ってますか? 私はNintendo Switch ドックのUSBポートから取ってます。
それでは、このはちゃんをメイドにしましょう。今回はDiscordのBotにします。
本当はLINEとかの方が親しい感じあって良いのですが時間がないので手慣れた方で行きます。
作っていく
環境の準備
とりあえずサクッとBotアカウントを作ります。アイコンは公式の壁紙から持ってきました。確かバレンタインシーズンで、チョコ作ってるイラストです。画像の直リンク持ってくるのも嫌なのでスクショ貼っておきますね
この時、Public Botをオフにして、
あとは、メイドさんを常駐いつでも話せるような環境も必要ですね。VPSです。VPSを契約するのです。私は自宅で常時起動でも良いのですが一応契約しました(ケチなのでVer.2.0のサーバーです)
あとはSwitchBotのAPIを触れる準備をしておきましょう、各種APIキーですね。この辺は各位ggってください。時間がない(ry
コードを書こう
今回はDiscord.js V14を使います。1から書くのは割とガチで数年ぶりなのでだいぶ時間かかってました。
それ以外には、dotenvとnode-fetchもインストールしておきます。正直公開しなければdotenvいらない
後にGitHubリポジトリのURLを貼っているので、cloneした場合は npm install --prefix .
などしてよしなにどうぞ。
とりあえず今回実装する機能はこんな感じ。
- DMを送るとエアコンと電気をつけて待ってくれる
- 寝る挨拶をすると電気を常夜灯にしてくれる
数が少ないですか? その辺は皆さんの教育で何とかしてください。私には時間(ry
ではコードを書きましょう。こうなりました
require("dotenv").config();
const token = process.env.DISCORD_TOKEN;
const master = process.env.CONOHA_MASTER;
const swtoken = process.env.SWBOT_TOKEN;
const swsecret = process.env.SWBOT_SECRET;
const swaircon = process.env.SWBOT_DEVICEID_AIRCON;
const swlight = process.env.SWBOT_DEVICEID_LIGHT;
const swoyasumi = process.env.SWBOT_SCENEID_OYASUMI;
const Discord = require("discord.js");
const intents = [Discord.GatewayIntentBits.MessageContent, Discord.GatewayIntentBits.DirectMessages];
const partials = [Discord.Partials.Channel, Discord.Partials.Message]
const client = new Discord.Client({ intents: intents, partials: partials });
const fetch = require("node-fetch");
const crypto = require("crypto");
client.on("ready", () => {
console.log("Discordへの接続完了です!:", client.user.tag);
});
client.on("messageCreate", message => {
if (message.author.bot) return;
if (message.channel.type !== Discord.ChannelType.DM) return;
if (message.author.id !== master) return;
// メッセージ内容で分岐
if (message.content.includes("もうすぐ帰る")) {
const replyMsg = "了解です!お部屋の準備してお待ちしてますね!";
const errorMsg = "了解です!お部屋の準備して……\nあれ?リモコンの電池が切れちゃってるみたいです……。";
// まずは部屋の明かりをつける
const body = {
"command": "調光1", // まあ普通はturnOnでええやろな
"commandType": "customize"
}
const result1 = switchBotApiRequest(`devices/${swlight}/commands`, body);
if (result1 == false) {
message.channel.send(errorMsg);
} else {
const currentMonth = new Date().getMonth() + 1; // 1月の返り値は0なんですね (1敗)
if ([1, 2, 3, 11, 12].includes(currentMonth)) {
// 暖房をつける
const body = {
"command": "setAll",
"parameter": "24,5,1,on",
}
// const result2 = switchBotApiRequest(`devices/${swaircon}/commands`, body);
if (result1 == false) {
message.channel.send(errorMsg);
} else message.channel.send(replyMsg);
} else if ([7, 8, 9].includes(currentMonth)) {
// 冷房をつける
const body = {
"command": "setAll",
"parameter": "22,2,1,on",
}
const result3 = switchBotApiRequest(`devices/${swaircon}/commands`, body);
if (result3 == false) {
message.channel.send(errorMsg);
} else message.channel.send(replyMsg);
} else message.channel.send(replyMsg);
}
} else if (message.content.includes("おやすみ")) {
// シーンでやってみる
// ID取るのは…もうv1のAPIで良いんじゃないですか(諦)
const result = switchBotApiRequest(`scenes/${swoyasumi}/execute`);
if (result == false) {
message.channel.send("おやすみなさいです、ごsy…\nあれ?リモコンの電池が切れちゃってるみたいです……。");
} else message.channel.send("おやすみなさいです、ご主人さま!");
}
});
function switchBotApiRequest(path, bodyJson = null) {
const token = swtoken;
const secret = swsecret;
const t = Date.now();
const nonce = crypto.randomUUID();
const signTerm = crypto.createHmac("sha256", secret)
.update(Buffer.from(token + t + nonce, "utf-8"))
.digest();
const sign = signTerm.toString("base64");
const headers = {
"Authorization": token,
"sign": sign,
"nonce": nonce,
"t": t,
"Content-Type": 'application/json',
}
const fetchOpts = {
method: "POST",
body: JSON.stringify(bodyJson),
headers: headers
}
fetch(`https://api.switch-bot.com/v1.1/${path}`, fetchOpts).then(res => {
if (!res.ok) {
console.error("Fetch failed...");
return false;
} else return true;
})
}
client.login(token);
各家電のIDは事前にcurlでもして取っておきましょう。
そしてエアコンのパラメータセットがややこしい。{temperature},{mode},{fan speed},{power state}
の順番です。
modeは0/1 (自動), 2 (冷房), 3 (除湿), 4 (送風), 5 (暖房) で
fan speed は1 (自動), 2 (遅), 3 (中), 4 (速) のはず。
全部見たい方は以下のRepoを見てください。
メイドを召喚
準備ができたら早速メイドさんを呼ぶ呪文を唱えましょう。
$ node index.js
Discordへの接続完了です!: メイドのこのは#0000
無事起動ログが流れてきました。
あとは適当なサーバーにBotを入れて、DMを送れば……
ちゃんと部屋の明かりとエアコンがつきました!あとはこれを帰宅前に送ればOKです!!
もちろん「おやすみ」と言えば、ちゃんと明かりが消えます!
これで毎日、おうちに帰ればあったか我が家が待っていますね!!
…………このはちゃんがいないって? 自分でコスプレでもしてください (公式衣装欲しい)
さいごに
結局間に合いませんでした。懺悔
バイトが長引きまくって今終わりました
明日公開のConoHa Advent Calendarの記事は書き終わっていません対戦よろしくお願いします
— 鮎月|あゆつき⭐️👾@冬コミ2日目-西か19b (@ayutsuki_vrc) December 9, 2024
ちなみにですが、今年も何故かあのNGワードの設定がありません。
なので去年同様叫ぼうと思いましたが、このはちゃんからお叱りを受けました。なんでや。
色々感想あったのに最後のおっs・・・で全部飛んだ
— 美雲このは☁️💙 (@MikumoConoHa) December 12, 2023
色々感想あったのに最後のおっs・・・で全部飛んだ
— 美雲このは☁️💙 (@MikumoConoHa) December 12, 2023
なので、去年も今年も設定されていない理由を考えましょう。
…
……
………
もしかして、変わりました? 中のひt
明日 (11日) の記事は、@LemoNyanさんの「お天気このはちゃん!」です。
こちらもぜひご覧ください!
このコードを書くにあたり、以下の記事を参考にしました。ありがとうございます。