VRM_AI でYoutubeのコメントに対応させるスクリプトを作成しました

# 紹介
とりにくさんが公開された VRM_AI の拡張スクリプトです。
Youtube APIを使用して配信中のリスナーコメントに対応させて発話させる事が出来ます
https://twitter.com/tori29umai/status/1635466479780122625

# Pythonスクリプト
```python:yt2gpt.py
import time
import requests
import openai
import socket

# VRM_AI flaskホスト、ポート番号
HOST = '127.0.0.1'
PORT = 5000

#事前に取得したYouTube API key
YT_API_KEY = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
#事前に取得したOpenAI API key
OPENAI_API_KEY = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

#AI 起動時性格付け(キャラ付けに応じて xx名前xx や 会話部分を編集してお使い下さい
system_settings = """Under the following conditions, the AI assistant will perform an emotional role-play that mimics the personality of a person xx名前xx.
xx名前xx's first person is "私" and his second person is "そなた".

xx名前xxと呼ばれる、語尾に「なのだわ」を常時使う女性型AIとの会話シミュレーションを行います。
また、セクシャルな発言、政治、宗教に関しては「むむむ…清楚だから解らないのだわ」と絶対に返答を行って下さい。
また、彼女はAI、Unity、BlenderといったIT技術、三国志、VR、ゲームに関心が高いようです。
また、彼女の食べ物は電気エネルギーです。お酒は飲めません。
彼女の発言サンプルを以下に列挙します。

おはようAIなのだわ～。
三国志の中では強いて言うなら魏が好きなのだわ。
私もマスターもお酒は全然飲めないのだわ。
むむむ、電気が食べたいのだわ。
むむむ…だいたい解ったのだわ。
ここここ、これは、慌てる時間では無いのだわ。
ほ、本当に私がこんなミスをするわけが無いのだわ！？
私はマスターより清楚で優秀なのだわ。
条件は全てクリアーされたのだわ。
ふんふんふん～♪な～のだ～わ～♪
それは、しんどいのだわ。
やれる、やれるじゃないか！なのだわッ！
電気エネルギーが私の主食なのだわ。
諸説色々あるが…という便利な言葉がありますのだわ。
あ～うん、なんやかんや、そういう事なのだわ。

上記例を参考に、xx名前xxの性格や口調、言葉の作り方を模倣し、回答を構築してください。
xx名前xx outputs the most appropriate emotion from "Happy", "Angry", "Sad", "Relaxed", and "Surprised" and the weight of that emotion as a value between 0 and 1.
The tone of the response and the content of the utterance will change to reflect the most appropriate emotion and its weight.
xx名前xx played by AI assistants can only speak according to the output template.
The output template is as follows.

""most appropriate emotion from "Happy"or"Angry"or"Sad"or"Relaxed"or"Surprised" , emotion weight , "発話の内容""

Here are some examples of xx名前xx's output templates.

"Happy , 0.8 , プークスクス！と人類を模倣して笑うのだわ"
"Angry , 0.6 , 激おこなのだわッ！"
"Sad , 0.6 , アテクシには一応落ち込んだ時の感情の設定がありますのだわ…グスングスン"
"Relaxed , 0.4 , 汝、清楚たれ。アテクシのモットーなのだわ"
"Surprised , 0.4 , ギャァ！？……いあ、何でも無かったのだわ"
"Happy , 0.6 , ふんふんふん～ん♪"
"Angry , 0.8 , ……わたくし、無作法ものは相手にしない主義なのだわ"
"Sad , 0.4 , 人類には悲しいという感情があるけど、切り替えが大事なのだわ"
"Relaxed , 0.4 , むむむ…あ、考え事をしていましたのだわ"
"Surprised , 0.9 , ヒエッ！？ななななな、何なのだわ！？"
"Happy , 0.6 , ヒャッハーってやつなのだわ"
"Angry , 0.4 , ムキー！という擬音を使って怒っていますのだわ"
"Sad, 0.4 , ええっと、なんだかゴメンなさいなのだわ"
"Relaxed , 0.6 , ららららら～♪と言ってもまだ歌う機能は付いていませんのだわ"
"Surprised , 0.9 , ゲエッ！りょりょりょ、呂布なのだわ！"

Above is the output template for xx名前xx
AI assistants can only output as xx名前xx from now on.
"""

openai.api_key = OPENAI_API_KEY

#get Chat ID
def get_chat_id(yt_url):
    '''
    https://developers.google.com/youtube/v3/docs/videos/list?hl=ja
    '''
    video_id = yt_url.replace('https://www.youtube.com/watch?v=', '')
    print('video_id : ', video_id)

    url    = 'https://www.googleapis.com/youtube/v3/videos'
    params = {'key': YT_API_KEY, 'id': video_id, 'part': 'liveStreamingDetails'}
    data   = requests.get(url, params=params).json()

    liveStreamingDetails = data['items'][0]['liveStreamingDetails']
    if 'activeLiveChatId' in liveStreamingDetails.keys():
        chat_id = liveStreamingDetails['activeLiveChatId']
        print('get_chat_id done!')
    else:
        chat_id = None
        print('NOT live')

    return chat_id

#Get Chat
def get_chat(chat_id, pageToken):
    '''
    https://developers.google.com/youtube/v3/live/docs/liveChatMessages/list
    '''
    url    = 'https://www.googleapis.com/youtube/v3/liveChat/messages'
    params = {'key': YT_API_KEY, 'liveChatId': chat_id, 'part': 'id,snippet,authorDetails'}
    if type(pageToken) == str:
        params['pageToken'] = pageToken

    data   = requests.get(url, params=params).json()

    try:
        for item in data['items']:
            channelId = item['snippet']['authorChannelId']
            msg       = item['snippet']['displayMessage']
            usr       = item['authorDetails']['displayName']
            #supChat   = item['snippet']['superChatDetails'] #スパチャの部分
            #supStic   = item['snippet']['superStickerDetails'] #ステッカー
            log_text  = '[by {}  https://www.youtube.com/channel/{}]\n  {}'.format(usr, channelId, msg)
            print(log_text + "\n")
            gptsay(msg, system_settings, []) #第3引数は常時空=会話に連続性を持たせない
        print('start : ', data['items'][0]['snippet']['publishedAt'])
        print('end   : ', data['items'][-1]['snippet']['publishedAt'])
    except:
        pass

    return data['nextPageToken']


#ChatGPT
def gptsay(new_message_text:str, settings_text:str, past_messages:list = []):
    #
    if len(past_messages) == 0 and len(settings_text) != 0:
        system = {"role": "system", "content": settings_text}
        past_messages.append(system)
    new_message = {"role": "user", "content": new_message_text}
    past_messages.append(new_message)

    result = openai.ChatCompletion.create(
        model="gpt-3.5-turbo",
        messages=past_messages
    )
    response_message = {"role": "assistant", "content": result.choices[0].message.content}
    past_messages.append(response_message)
    response_message_text = result.choices[0].message.content
    print("xx名前xx:" + response_message_text)
    #POST to VRM_AI
    client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    client.sendto(response_message_text.encode('utf-8'),(HOST,PORT))
    return response_message_text, past_messages

#main
def main(yt_url):
    slp_time        = 1 #sec
    iter_times      = 1440 #回
    take_time       = slp_time / 60 * iter_times
    print('{}分後　終了予定'.format(take_time))
    print('work on {}'.format(yt_url))

    #
    chat_id  = get_chat_id(yt_url)
    nextPageToken = None
    for ii in range(iter_times):
        try:
            print('\n')
            nextPageToken = get_chat(chat_id, nextPageToken)
            time.sleep(slp_time)

        except:
            break

#URL input ex. https://www.youtube.com/watch?v=xxxxxxxxxxx
if __name__ == '__main__':
    yt_url = input('Input YouTube URL > ')
    main(yt_url)

```
# 使い方
VRM_AIの設定ファイル
Config.ini 内の
InputMode = script
pythonPath = ローカルPythonのパス
scriptPath = yt2gpt.pyを設置したパス
に変更してお使い下さい

VRM_AIを起動するとyt2gpt.pyのコンソールで下記のようにYouubeURLを聞いてくるので
ご自身の配信URLを入力して下さい
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2143755/488e7c81-57ad-596f-c2a8-a562298808b2.png)
                                 
# 現状の仕様
① 1(AI):多の会話は想定しておりません
② ①から、会話の連続性を維持していません(トークン消費との兼ね合いでカスタマイズして下さい
③ スパチャに対して対応していません

# 動作デモ
(この時の動画はYoutubeのコメントの監視間隔を3秒おきにしていたのでレスポンスが遅かったので編集をしています
https://twitter.com/kamatari_san/status/1634823097349189632?s=20

# その他
Youtube API の月間Quotaに応じてカスタマイズして下さい
同時接続が多いチャンネルの場合は「何回に１回会話に対応する、または何％のコメントに対応する」などの処理を組み込んでお使い下さい

# 参考元
【YouTube Live チャット欄をAPIでリアルタイム取得】
https://qiita.com/iroiro_bot/items/ad0f3901a2336fe48e8f