VRChat APIで特定のGroupに通知する方法【2段階認証対応】

# はじめに

VRChat APIを使用して外部から通知でも飛ばそうかと思います。
VRC+を契約してgroup作れるようになったので備忘録です。

嘘です本当はあっきーすんの地震情報通知グルをみて憧れて作ったdiscordの通知をVRChatに飛ばすbot作ったときの副産物です

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">震度4以上の地震情報が流れてくるグループです<br>試験運用中なので参考程度でよろです<br>出入り自由なので入ったり入らなかったりしてください<a href="https://t.co/2Z0kDcHIyj">https://t.co/2Z0kDcHIyj</a></p>&mdash; axtuki1／あっきー (@axtuki118) <a href="https://twitter.com/axtuki118/status/1601646364366020608?ref_src=twsrc%5Etfw">December 10, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

# 参考
:::note warn
VRChat APIは正式に認められた機能じゃないので気をつけましょう
:::

https://vrchatapi.github.io

~~この記事見なくても↑のとおりにすればいい~~

# やりたいこと

これです
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/499397/d5a05e3a-85f9-6178-6a74-dfe2100a8a94.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/499397/9068cf96-4bdc-a697-21dc-8bcf07a37df1.png)




# 認証

`APIKEY + ユーザーネーム + パスワード` で`authcookie`が発行されます


### APIKEY取得
`JlE5~...`固定なので別にいらないんですけどね

https://api.vrchat.cloud/api/1/config

↑にブラウザとかcurlとかでアクセスして出てきたclientApiKeyのやつです
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/499397/57c5ce3d-5268-0516-4887-0a23f2dd373d.png)

### authcookie取得


authcookieは
```
https://api.vrchat.cloud/api/1/auth/user
```
にGETでレスポンスについてるcookieの中にあります
[ドキュメント](https://vrchatapi.github.io/docs/api/#get-/auth/user)


```python: get_authcookie.py
import requests

UN = "ユーザ名"
PW = "パスワード"
APIKEY = "JlE5Jldo5Jibnk5O5hTx6XVqsJu4WJ26" #さっきのやつ

r = requests.get("https://api.vrchat.cloud/api/1/auth/user",
                headers = {"User-Agent": UN},
                data = {"apiKey": APIKEY},
                auth = (UN,PW))

print(r.cookies["auth"])
```
`authcookie_`から始まるやつが出てくると思います。それです。



### 2段階認証
2段階認証とかweb版をログアウトした人はgoogle authenticatorでコード拾ったりとかメールとかからコードが送られてきたりすると思います。その状態だと`authcookie`がまだ`有効化`されていないので`authcookie`を送られてきたコードと共に

2段階認証を有効にしているならこっち ↓ に
```
https://api.vrchat.cloud/api/1/auth/twofactorauth/totp/verify
```

2段階認証を有効にしていなくてメールにコードが届く人はこっち ↓ に
```
https://api.vrchat.cloud/api/1/auth/twofactorauth/emailotp/verify
```
POSTすることで`authcookie`を有効化できます

[ドキュメント (2段階)](https://vrchatapi.github.io/docs/api/#post-/auth/twofactorauth/totp/verify)
[ドキュメント (メール)](https://vrchatapi.github.io/docs/api/#post-/auth/twofactorauth/emailotp/verify)

```python:2fa.py
#2段階認証はこっち
import requests

f2acode = input("f2acode: ")
cookie = "authcookie_**********"

requests.post("https://api.vrchat.cloud/api/1/auth/twofactorauth/totp/verify",
               headers={"User-Agent": UN,
                        "Content-Type": "application/json"},
               cookies={"auth": cookie},
               json={"code": f2acode})
```
```python:mailverify.py
#メール認証はこっち
import requests

f2acode = input("f2acode: ")
cookie = "authcookie_**********"

requests.post("https://api.vrchat.cloud/api/1/auth/twofactorauth/emailotp/verify",
               headers={"User-Agent": UN,
                        "Content-Type": "application/json"},
               cookies={"auth": cookie},
               json={"code": f2acode})
```



# グループに通知

:::note info
グループに通知するには自分がそのグループ内で通知を発信する権限(ロール)がある必要があります。
:::

グループに通知を飛ばすには認証で得た`authcookie`をタイトルや内容とともに
```
https://api.vrchat.cloud/api/1/groups/ """ グループID """ /announcement
```
にPOSTすることでできます。
[ドキュメント](https://vrchatapi.github.io/docs/api/#post-/groups/-groupId-/announcement)

グループIDはwebで開いたときのURLの`grp_`から始まるこれです
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/499397/be93e2f0-b08d-8fab-efdf-4ea427ffeff9.png)


これで

```python:group_announcement.py
import json

authcookie = "authcookie_**********"
groupId = "grp_**************"

title = "タイトルです"
text = "テキスト(内容)です"

requests.post(f"https://api.vrchat.cloud/api/1/groups/{groupId}/announcement",
                json.dumps({"title": title, "text": text, "sendNotification": True}),
                headers = {"User-Agent": UN,
                            "Content-Type": "application/json"},
                cookies={"auth": authcookie})
```
飛ぶとおもいます。
`imageId`というすでにアカウントにアップロードされてる画像をくっつけるパラメーターもあります。

# まとめたやつ
```python:main.py
import json
import requests

groupId = "grp_*****************"
title = ""
text = ""

UN = ""
PW = ""

base = "https://api.vrchat.cloud/api/1/"
headers = {"User-Agent": UN}

def getAuthCookie(UN:str, PW:str):
    r = requests.get(f"{base}auth/user",
                     headers = {"User-Agent": UN},
                     data = {"apiKey": "JlE5Jldo5Jibnk5O5hTx6XVqsJu4WJ26"},
                     auth = (UN,PW))
    print("getAuthCookie", r)
    return r.cookies["auth"]

def f2a(cookie:str):
    f2acode = input("f2acode: ")
    r = requests.post(f"{base}auth/twofactorauth/emailotp/verify",
                      headers=headers,
                      cookies={"auth": cookie},
                      json={"code": f2acode})
    print("f2a", r)

def annoucement(groupId:str, title:str, text:str, authCookie:str):
    def send():
        r = requests.post(f"{base}groups/{groupId}/announcement",
                          json.dumps({"title": title, "text": text, "sendNotification": True}),
                          headers = {"User-Agent": UN, "Content-Type": "application/json"},
                          cookies={"auth": authCookie})
        print("annoucement", r.text)
        return r

    if json.loads(send().text)["error"]["message"] == "\"Requires Two-Factor Authentication\"":
        f2a(authCookie)
        send()

annoucement(groupId=groupId,
            title=title,
            text=text,
            authCookie=getAuthCookie(UN=UN, PW=PW))
```