使い魔ボットを創ろう - TwitchIOでシンプルなTwitchボットを作る

Twitchをやってると何かとサポートしてくれるボットが欲しくなったので作ることしました。これはまず作ってみたChatGPTで雑談するボットです。にゃんこ仕様ですにゃん

![TwitchIO-SimpleSample_00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33704/8350c9f9-afd5-d480-5a9c-c9f080521e90.png)

`TwitchIO` ライブラリを使うと簡単にボットを作れるので、まずはシンプルなものを説明します。そのうちChatGPT入も解説します。

2024-12-03 翻訳AIのコードを公開しました。開発中のボットAIの会話も見れます。

https://qiita.com/akiraak/items/9a5e17e3194057a1934c


今回説明することは４つ

+ ACCESS_TOKEN の取得
+ プログラミング
    + チャンネルへのJoin
    + コメントが書き込まれたらコメントを返す
    + コマンドが入力されたらコメントを返す

## ACCESS_TOKEN の取得

ボットをチャンネルにログインさせるには ACCESS_TOKEN が必要になります。ボット用のアカウントでログインした状態で以下のURLにアクセスし、画像の手順をふみます。
[Twitch Chat OAuth Password Generator](https://twitchapps.com/tmi/)

![Qiita_Twitch_OAuth_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33704/7c909849-f2a5-f5a2-8700-47e08a0a190e.png)

![Qiita_Twitch_OAuth_02-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33704/f9c6e648-1ef3-a106-aad3-3defc67b5214.png)

![Qiita_Twitch_OAuth_02-03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33704/3558905f-928f-aeb5-bad5-05a1db696e2c.png)

表示されたものをコピっておいてください。

# プログラミング

まずは `TwitchIO` をインストールです。

```sh
$ pip install twitchio
```

全体のコードを見てから１つ１つ追って見ていきます。

```py
from twitchio import Channel, Message
from twitchio.ext import commands


WTITCH_ACCESS_TOKEN = "XXXXXXXXXXXXXXXXXXXXXXXX"
LOGIN_CHANNEL = "akiraaak"
COMMAND_PREFIX = "!"

class Bot(commands.Bot):

    def __init__(self):
        super().__init__(
            token=WTITCH_ACCESS_TOKEN,
            prefix=COMMAND_PREFIX,
            initial_channels=[LOGIN_CHANNEL]
        )

    async def event_channel_joined(self, channel: Channel):
        print(f"ログインしました。チャンネル名: {channel.name}")
        for chatter in channel.chatters:
            print(f'ユーザーID: {chatter.id}')
            print(f'ユーザー名: {chatter.name}')
            print(f'表示名: {chatter.display_name}')
        await channel.send(f"{chatter.name}があらわれた！")

    async def event_ready(self):
        print("全てのチャンネルにログインしました。")
        print(f'ユーザーID: {self.user_id}')
        print(f'ユーザー名: {self.nick}')

    # コメントが書き込まれると呼び出される
    async def event_message(self, message: Message):
        # ボットの発言は無視する
        if message.echo:
            return

        # メッセージがコマンドであれば、ここで処理を終了
        if message.content.startswith(COMMAND_PREFIX):
            await self.handle_commands(message)
            return

        print(f'ユーザーID: {message.author.name}')
        print(f'ユーザー名: {message.author.display_name}')
        # オウム返し
        await message.channel.send(f"{message.content}")

    # !hello こんにちは
    @commands.command(name='hello')
    async def cmd_hello(self, ctx: commands.Context):
        await ctx.send(f'Hello! {ctx.author.name}! {ctx.message.content}')


def main():
    bot = Bot()
    bot.run()


if __name__ == "__main__":
    main()
```

さきほど取得した `ACCESS_TOKEN` は `WTITCH_ACCESS_TOKEN` に設定してください。

Twitch のボットは `TwitchIO` を使うととても簡単に作れます。`commands.Bot` を継承した `Bot` を定義して、そのインスタンスを作成し `run()` するだけです。

```py
class Bot(commands.Bot):

    def __init__(self):
        super().__init__(
            token=WTITCH_ACCESS_TOKEN,
            prefix=COMMAND_PREFIX,
            initial_channels=[LOGIN_CHANNEL]
        )
```

```py
bot = Bot()
bot.run()
```

`Bot` クラスのコンストラクタには、ボットのACCESS_TOKEN、Joinするチャンネル、コマンドラインのPrefixを指定します。

ACCESS_TOKEN は最初に取得したものですね。

チャンネルは配信のチャンネルで、僕のチャンネルだと https://www.twitch.tv/akiraaak なので `akiraaak` になります。

コマンドラインのPrefixですが、のちのち出てきますが、ボットにはコマンドを設定できます。ビートセイバーのボットだと、`!bsr` `!bomb` `!link` などがあるんですが、この先頭の `!` になります。`?`なんかも一般的です。 まずは`!`を指定しておけばいいとおもいます。


## チャンネルへのJoin

`run()` を実行してチャンネルに入るとコールバック関数が呼ばれます。今回は２つ定義しています。

コンストラクタのチャンネルの指定は配列でした。 `initial_channels=[LOGIN_CHANNEL]` コールバック関数はそれぞれのチャンネルに入った後に呼ばれるものと、全てに入ったあとに呼ばれるものと２つあります。

それぞれのチャンネルで呼ばれるコールバック関数
```py
async def event_channel_joined(self, channel: Channel):
    print(f"ログインしました。チャンネル名: {channel.name}")
    for chatter in channel.chatters:
        print(f'ユーザーID: {chatter.id}')
        print(f'ユーザー名: {chatter.name}')
        print(f'表示名: {chatter.display_name}')
    await channel.send(f"{chatter.name}があらわれた！")
```

全てのチャンネルに入ったあとに呼ばれるコールバック関数
```py
async def event_ready(self):
    print("全てのチャンネルにログインしました。")
    print(f'ユーザーID: {self.user_id}')
    print(f'ユーザー名: {self.nick}')
```

ただし、event_channel_joined()のユーザー情報は正しいものが入ってきてないように思います。id,display_name が None だったり、全てのユーザーが返ってこなかったり。挙動がよくわかりません。

`await channel.send()` を使うことでコメントを書くことができます。 
`await channel.send(f"{chatter.name}があらわれた！")`

![TwitchIO-SimpleSample_03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33704/4cc1d225-177f-6eac-6ad2-be4b4bc3e039.png)


## コメントが書き込まれたらコメントを返す

少し複雑なコードになっているので、先に下のコマンドの説明に目を通した方がいいかもしれません。

```py
    # コメントが書き込まれると呼び出される
    async def event_message(self, message: Message):
        # 1.ボットの発言は無視する
        if message.echo:
            return

        # 2.メッセージがコマンドであれば、ここで処理を終了
        if message.content.startswith(COMMAND_PREFIX):
            await self.handle_commands(message)
            return

        print(f'ユーザーID: {message.author.name}')
        print(f'ユーザー名: {message.author.display_name}')
        # オウム返し
        await message.channel.send(f"{message.content}")
```

コメントが書き込まれたらコメントを返すだけなのですが、最初に大切な儀式が２つあります。

1. ボット自身が書き込んだコメントの場合は抜ける
2. コメントがコマンドだったらコマンド処理を実行して抜ける

この２つにだけ気をつければ、あとは `message.author` にユーザー情報が入ってくるのと `message.content` にコメントが入ってくるのでそれを処理すれば大丈夫です。

`await message.channel.send()` でそのままオウム返しをしてます。

![TwitchIO-SimpleSample_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33704/b38cbc48-802d-93e7-337f-417074fa61f4.png)

## コマンドが入力されたらコメントを返す

ボットにはコマンドという機能があります。コメントが`prefix`（今回は！）から始まった特定の単語のときに関数が呼ばれます。


このコマンドは `!hello` というコマンドになります。`!hello こんにちは` とコマンドの後ろに文字を続けて使うこともできます。ビートセイバーだと `!bsr 2142b`なんて使い方がされてますね。

https://bsaber.com/songs/2142b/

```py
# !hello こんにちは
@commands.command(name='hello')
async def cmd_hello(self, ctx: commands.Context):
    await ctx.send(f'Hello! {ctx.author.name}! {ctx.message.content}')
```

`!hello こんにちはこんにちわ` と入力されたので `Hello! akiraaak !hello こんにちはこんにちわ` と返しています。

![TwitchIO-SimpleSample_05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33704/d244599b-0a88-5de0-80d6-078c2c390620.png)


# おわりに

`TwitchIO` を使うととても簡単にTwitchボットが作れると思います。どうぞお試しくださいまし。

Twitchボット開発風景の配信もやってるのでよかったらみにきね
[あキら](https://www.twitch.tv/akiraaak)

https://www.twitch.tv/akiraaak