Discord.jsでReaction数を取得する

# 背景
<!-- 発端や概要を記載 -->
この記事は[豊田高専 Advent Calendar 2021](https://qiita.com/advent-calendar/2021/nit_toyota) 6日目(3本目)の記事です。

専攻科の同級生に誘われたため、このお祭りに参加しました。

私が所属してるDiscordのコミュニティでは、見つけた面白そうな技術記事やレポジトリをとりあえず投稿するチャンネルがあります。
通称、「後で読む」チャンネルです。
各自が空いた時間に投稿されたものを読み返して為になると感じたら、チャンネルの参加者はその投稿に対してReactionを付けます。
他の参加者は後から有益な記事をReaction数という指標で探すことができるようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1018861/07ac0c49-e8f2-07d2-5787-64e06354790c.png)

本当に良い記事かを精査せずにとりあえず投稿できることが、このチャンネルの目的ですが、時が経つにつれて記事が玉石混合になり、辿るのも一苦労という現状がありました。

そこで、**一定のReaction数があった場合にその投稿をピン止めをする** Botを作成することにしました。

# 目次
<!-- タイトルとアンカー名を編集 -->
1. [使用した技術](#1-使用した技術)
1. [Reactionイベントの取得](#2-Reactionイベントの取得)
1. [Reaction数を取得](#3-Reaction数を取得)
1. [ピン止めをする](#4-ピン止めをする)
1. [終わりに](#5-終わりに)


<!-- 各チャプター -->

#1. 使用した技術

[Discord.js v13.3.1](https://discord.js.org/#/)を用いて開発し、作成したBotのデプロイ先として[heroku](https://jp.heroku.com/nodejs)を使用しました。

また、ローカルでのテストを動かすために[dotenv](https://github.com/motdotla/dotenv)を使用しました。
[dotenv](https://github.com/motdotla/dotenv)はソースコードに埋め込みたくないため、dotenvをdevDependenciesに記述し、npm run devで起動するようにpackage.jsonに記述しました。

```json
{
"devDependencies": {
		"dotenv": "^10.0.0"
},
"scripts": {
		"dev": "npm run build && node -r dotenv/config dest/index.js"
	},
}
```

参考：

https://blog.leko.jp/post/you-might-not-need-dotenv-in-source/

#2. Reactionイベントの取得

投稿にReactionがついたイベントを取得するには以下のコードを使用します。

Discord.jsのv13から追加されたIntentsの```Intents.FLAGS.GUILD_MESSAGE_REACTIONS```が必要です。

また、Bot起動以前の投稿に対してもイベントが発生するように、partialsに```'MESSAGE', 'CHANNEL', 'REACTION'```を追加しました。

```ts
import { Client, Intents } from 'discord.js';
const client = new Client({
	intents: [
		Intents.FLAGS.GUILDS,
		Intents.FLAGS.GUILD_MESSAGES,
		Intents.FLAGS.GUILD_MESSAGE_REACTIONS
	],
	partials: ['MESSAGE', 'CHANNEL', 'REACTION']
});
// メッセージにリアクションがついた
client.on('messageReactionAdd', async (reaction, user) => {
		console.log(`${user.username} reacted with "${reaction.emoji.name}".`);
});

// メッセージのリアクションが外れた
client.on('messageReactionRemove', async (reaction, user) => {
	console.log(`${user.username} removed their "${reaction.emoji.name}" reaction.`);
});
```
参考：

https://discordjs.guide/popular-topics/reactions.html#awaiting-reactions


#3. Reaction数を取得

### Reactionされた数を取得
```ts
client.on('messageReactionAdd', async (reaction, user) => {
	console.log(reaction.count);
});
```
Reaction.countでReactionされたのと同じReactionの数を取得できます。例えば、👀のReactionを押したときに、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1018861/76028462-e05b-0581-1d34-6a5d2892700d.png)
↑で押されたReaction👀と同じReactionの数である3が取得できて
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1018861/b8ee9a77-7e0d-dd96-8e07-4a69c7b52be7.png)
↑で1が取得できます。

### 特定のReaction数を取得

特定のReaction(例えば👍だけに反応)を取得したいときは```.cache.get('👍')?.count```で取得できます。

```ts
reaction.message
		.fetch()
		.then(async (message) => {
			const reactionCount = message.reactions.cache.get('👍')?.count;
.catch((e) => {
			console.error('Something went wrong when fetching the message: ', e);
		});
})
```

#4. ピン止めをする

Reactionされた投稿に対してピン止めを行いました。

### Reactionされたメッセージをピン止めする

```ts
reaction.message
		.fetch()
		.then(async (message) => {
		// メッセージをピン止め
			await message.pin();
		})

```

### Reactionされたメッセージのピン止めを外す

```ts
reaction.message
		.fetch()
		.then(async (message) => {
		// ピン止めを外す
			await message.unpin();
		})

```

#5. 終わりに

作成した機能は以下のレポジトリのBotに追加しました。

https://github.com/nori3636/discordbot_codehighlight

実際に運用してみて、コミュニティが活発に活動するとログがむちゃくちゃ出てしまうので、Discordではなく外部に出力するか、一定周期(週一とか)で一気に取得する方がいいかもですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1018861/a39edef7-c545-d659-4f6f-046a03e281c0.png)


今回みたいに自分の所属している(いた)コミュニティが盛り上がるイベントには積極的に参加していきたいと思ってます。
ぜひ誘ってください。