グローバルチャットとは
グローバルチャットとは、Discordのチャンネルを跨いで会話出来るチャットの事です。
特定のチャンネルのメッセージをBOTが受信し、他のチャンネルへメッセージを転送することで、別のサーバーにいる相手とも、あたかも同じチャンネルで会話しているような感覚でチャットを行えます。
スーパーグローバルチャットとは
従来のグローバルチャットでは、BOTがメッセージを仲介して他のチャンネルへと送信する仕組みのため、同じ会話に参加したければ、同じBOTを導入する必要があります。
また、グローバルチャットを導入しているBOTが増えすぎたために、結局会話が分散してしまい、グローバルチャットをサーバーに導入しようと思っても、どのBOTを入れれば良いのか混乱してしまいます。
そこで、BOT間でも連携を行い、更に大きなグローバルチャットを作ろうと考えたのが、スーパーグローバルチャットです。
スーパーグローバルチャットでは、おおよそこのような形式でJSONメッセージを作成し、特定のチャンネルに投稿することで、BOT間でメッセージを交換しています。
{
"type": "message",
"userId": "607645717623996426",
"userName": "つきこう",
"userDiscriminator": "2710",
"userAvatar": "a118cc1ef21fcbac75764483108888fb",
"isBot": false,
"guildId": "706543524958699570",
"guildName": "Newグローバルチャットサーバー",
"guildIcon": "db2be882afde5818366405376458a774",
"channelId": "707158194572623903",
"channelName": "kensaku-sgc",
"messageId": "773865745187733514",
"content": "Hello World",
"reference": "877075663352389672",
"attachmentsUrl": [
"https://cdn.discordapp.com/attachments/771644708048470046/771652380143517706/video0.mov",
"https://cdn.discordapp.com/attachments/771644708048470046/771652381800398868/video1.mov"
]
}
スーパーグローバルチャットはこのサーバーで導入しております。
動作感の確認や各種ご連絡はこちらへどうぞ
The Global Chat's Guild
この記事について
おおよそのスーパーグローバルチャットの概要を説明したところで、いよいよグローバルチャットの作成に進みましょう。
この記事では、いくつかのステップに分けて、Discord.pyでの他のチャンネルへのメッセージ転送、グローバルチャットの作成、スーパーグローバルチャットの導入と進んでいきます。
この記事の前提条件として、下記を想定しています。
- Pythonがなんとなく扱える
- Discord.pyを導入し、BOTを動作させることができる
- 埋め込み(Embed)でグローバルチャットの作成 (今回はWebhookは扱わない)
メッセージを他のチャンネルに転送する機能の作成
まずは、発言したことをそのまま同じ名前のチャンネルに送信する機能の作成です。
import discord
TOKEN = "<Token>" #トークンを入力
global_channel_name = "super-global-test" #設定したいチャンネル名を入力
client = discord.Client() #接続に必要なオブジェクトを生成
@client.event
async def on_message(message):
if message.channel.name == global_channel_name: #グローバルチャットにメッセージが来たとき
#メッセージ受信部
if message.author.bot: #BOTの場合は何もせず終了
return
#メッセージ送信部
for channel in client.get_all_channels(): #BOTが所属する全てのチャンネルをループ
if channel.name == global_channel_name: #グローバルチャット用のチャンネルが見つかったとき
if channel == message.channel: #発言したチャンネルには送らない
continue
await channel.send(message.content) #メッセージを送信
client.run(TOKEN)
メッセージを送信した事を知らせるリアクション
先程の await channel.send(message.content)
の後のfor文を抜けた位置に以下のものを付けるだけです。
await message.add_reaction('✅')
このままでは味気ないので、次はグローバルチャットのメッセージの土台作りです。
埋め込み(Embed)でメッセージを送信しよう
Discordには埋め込み(Embed)という機能があり、これを利用すると簡単にユーザーのメッセージを再現出来ます。
まずは、メッセージを埋め込み(Embed)にいれるだけ。
メッセージの送信部を以下のように変えます。
embed=discord.Embed(description=message.content, color=0x9B95C9) #埋め込みの説明に、メッセージを挿入し、埋め込みのカラーを紫`#9B95C9`に設定
await channel.send(embed=embed)
でも、これだと誰から来たメッセージなのか分からないので、ユーザーネームとアイコン画像を入れましょう。
ユーザーネームとアイコン画像の追加
先程の embed=discord.Embed(description=message.content, color=0x9B95C9)
の下に、以下のものを追加します。
embed.set_author(name=message.author.name,icon_url="https://media.discordapp.net/avatars/{}/{}.png?size=1024".format(message.author.id, message.author.avatar))
※Discord.py v2以降の場合
このままではエラーとなるため、以下のようにします。
if hasattr(message.author.avatar, 'key'): #アイコン画像が設定されているとき
embed.set_author(name=message.author.name,icon_url="https://media.discordapp.net/avatars/{}/{}.png?size=1024".format(message.author.id, message.author.avatar.key))
else:
embed.set_author(name=message.author.name)
これで送信者は分かるようになりました。
ちなみに、name=
の箇所を以下のようにすると Tsukikoh#0721
のような、Discordのユーザータグを表示させる事ができます。
name="{}#{}".format(message.author.name, message.author.discriminator)
サーバー名の設定
グローバルチャットは、他のチャンネルとの会話ができるので、時にどのサーバーから発言しているのか知りたくなることがあります。
先程のembed.set_author...
の次の行に下記のものを追加します。
embed.set_footer(text="{} / mID:{}".format(message.guild.name, message.id),icon_url="https://media.discordapp.net/icons/{}/{}.png?size=1024".format(message.guild.id, message.guild.icon))
※Discord.py v2以降の場合
このままではエラーとなるため、以下のようにします。
if hasattr(message.guild.icon, 'key'): #アイコン画像が設定されているとき
embed.set_footer(text="{} / mID:{}".format(message.guild.name, message.id),icon_url="https://media.discordapp.net/icons/{}/{}.png?size=1024".format(message.guild.id, message.guild.icon.key))
else:
embed.set_footer(text="{} / mID:{}".format(message.guild.name, message.id))
ちなみに、mID:
は、後でBOTがメッセージを検索しやすいようにするために、埋め込みに元のメッセージのIDを書き込んでいます。この機能は後ほど使います。
#画像を表示
埋め込みには、画像を挿入することもできます。時にはグローバルチャットで画像を共有したくなる事もあるでしょう。
先程の次の行に、以下のものを追加します。
if message.attachments != []: #添付ファイルが存在するとき
embed.set_image(url=message.attachments[0].url)
このパターンでは、添付ファイルが存在するときに、1枚目の画像だけを埋め込みに添付しています。
埋め込みでは、複数枚の画像を入れることができませんし、動画や音楽ファイル等もサポートしていません。そこは残念です。
メッセージの返信を取得
Discordには、最近返信機能が追加されました。
会話中に複数の話題が同時に持ち上がる事ってありますよね。グローバルチャットともなればなおさらだと思います。
そんなときに、どの会話に対して話しているのか示せると便利ですよね。
先程の次の行に以下のものを追加します。
if message.reference: #返信メッセージであるとき
reference_msg = await message.channel.fetch_message(message.reference.message_id) #メッセージIDから、元のメッセージを取得
if reference_msg.embeds and reference_msg.author == client.user: #返信の元のメッセージが、埋め込みメッセージかつ、このBOTが送信したメッセージのとき→グローバルチャットの他のサーバーからのメッセージと判断
reference_message_content = reference_msg.embeds[0].description #メッセージの内容を埋め込みから取得
reference_message_author = reference_msg.embeds[0].author.name #メッセージのユーザーを埋め込みから取得
elif reference_msg.author != client.user: #返信の元のメッセージが、このBOTが送信したメッセージでは無い時→同じチャンネルのメッセージと判断
reference_message_content = reference_msg.content #メッセージの内容を取得
reference_message_author = reference_msg.author.name+'#'+reference_msg.author.discriminator #メッセージのユーザーを取得
reference_content = ""
for string in reference_message_content.splitlines(): #埋め込みのメッセージを行で分割してループ
reference_content += "> " + string + "\n" #各行の先頭に`> `をつけて結合
reference_value = "**@{}**\n{}".format(reference_message_author, reference_content) #返信メッセージを生成
embed.add_field(name='返信しました', value=reference_value, inline=True) #埋め込みに返信メッセージを追加
ちなみに、行で分割して、各行の先頭に>
を入れて再度結合していますが、これは、Discordの埋め込みでは、Markdown形式の引用が利用出来るため、その形式に変換しています。
グローバルチャット完成
ここまでで、グローバルチャットの基本の機能が導入できました。
ここまでのプログラムまとめ
import discord
TOKEN = "<Token>" #トークンを入力
global_channel_name = "super-global-test" #設定したいチャンネル名を入力
client = discord.Client() #接続に必要なオブジェクトを生成
@client.event
async def on_message(message):
if message.channel.name == global_channel_name: #グローバルチャットにメッセージが来たとき
#メッセージ受信部
if message.author.bot: #BOTの場合は何もせず終了
return
#メッセージ送信部
for channel in client.get_all_channels(): #BOTが所属する全てのチャンネルをループ
if channel.name == global_channel_name: #グローバルチャット用のチャンネルが見つかったとき
if channel == message.channel: #発言したチャンネルには送らない
continue
embed=discord.Embed(description=message.content, color=0x9B95C9) #埋め込みの説明に、メッセージを挿入し、埋め込みのカラーを紫`#9B95C9`に設定
embed.set_author(name="{}#{}".format(message.author.name, message.author.discriminator),icon_url="https://media.discordapp.net/avatars/{}/{}.png?size=1024".format(message.author.id, message.author.avatar))
embed.set_footer(text="{} / mID:{}".format(message.guild.name, message.id),icon_url="https://media.discordapp.net/icons/{}/{}.png?size=1024".format(message.guild.id, message.guild.icon))
if message.attachments != []: #添付ファイルが存在するとき
embed.set_image(url=message.attachments[0].url)
if message.reference: #返信メッセージであるとき
reference_msg = await message.channel.fetch_message(message.reference.message_id) #メッセージIDから、元のメッセージを取得
if reference_msg.embeds and reference_msg.author == client.user: #返信の元のメッセージが、埋め込みメッセージかつ、このBOTが送信したメッセージのとき→グローバルチャットの他のサーバーからのメッセージと判断
reference_message_content = reference_msg.embeds[0].description #メッセージの内容を埋め込みから取得
reference_message_author = reference_msg.embeds[0].author.name #メッセージのユーザーを埋め込みから取得
elif reference_msg.author != client.user: #返信の元のメッセージが、このBOTが送信したメッセージでは無い時→同じチャンネルのメッセージと判断
reference_message_content = reference_msg.content #メッセージの内容を取得
reference_message_author = reference_msg.author.name+'#'+reference_msg.author.discriminator #メッセージのユーザーを取得
reference_content = ""
for string in reference_message_content.splitlines(): #埋め込みのメッセージを行で分割してループ
reference_content += "> " + string + "\n" #各行の先頭に`> `をつけて結合
reference_value = "**@{}**\n{}".format(reference_message_author, reference_content) #返信メッセージを生成
embed.add_field(name='返信しました', value=reference_value, inline=True) #埋め込みに返信メッセージを追加
await channel.send(embed=embed) #メッセージを送信
await message.add_reaction('✅') #リアクションを送信
client.run(TOKEN)
いよいよスーパーグローバルチャットの導入へ進みましょう。
スーパーグローバルチャットの導入
スーパーグローバルチャットとはで説明したように、スーパーグローバルチャットは、JSONメッセージをBOT間で送受信することにより、BOTを超えたグローバルチャットを実現しています。
スーパーグローバルチャット導入の為の下準備
まずは、スーパーグローバルチャットに必要となるパッケージを標準ライブラリからインポートします。
import json #JSONを扱うために使用
import urllib.parse #パーセントエンコーディングを扱うために使用
次に、JSONを試しに送受信するための仮のチャンネルを作成し、IDをコピーします。
ここでは、このJSONを扱うためのチャンネルのIDを878353900275642478
とします。
JSONのチャンネルを読み込むように条件変更
まずは、グローバル変数のglobal_channel_name = "super-global-test"
次の行に以下のものを追加します。
json_channel_id = 878353900275642478 #JSONチャンネルID
次に、プログラムを一部変更します。
if message.channel.name == global_channel_name: #グローバルチャットにメッセージが来たとき
if message.author.bot: #BOTの場合は何もせず終了
return
for channel in client.get_all_channels(): #BOTが所属する全てのチャンネルをループ
if message.channel.name == global_channel_name or message.channel.id == json_channel_id:#グローバルチャットかJSONチャンネルにメッセージが来たとき
if message.channel.name == global_channel_name: #グローバルチャットにメッセージが来たとき
if message.author.bot: #BOTの場合は何もせず終了
return
"""ここにJSONの送信部分を記述します"""
if message.channel.id == json_channel_id: #JSONチャンネルにメッセージが来たとき
if message.author == client.user: #メッセージ送信者がこのBOTの場合は何もせず終了
return
"""ここにJSONの受信部分を記述します"""
for channel in client.get_all_channels(): #BOTが所属する全てのチャンネルをループ
辞書型リストを作成し、JSONを出力
先程指定した場所に、下記のものを追加します。
"""ここにJSONの送信部分を記述します"""
dic = {} #辞書型リストを初期化
dic.update({"type": "message"}) #JSONで送信するデータの種類: メッセージ
dic.update({"userId": str(message.author.id)}) #ユーザーID
dic.update({"userName": message.author.name}) #ユーザーネーム
dic.update({"userDiscriminator": message.author.discriminator}) #ユーザータグ
dic.update({"userAvatar": message.author.avatar}) #ユーザーのアバター画像を示すキー
dic.update({"isBot": message.author.bot}) #ユーザーがBOTかどうか
dic.update({"guildId": str(message.guild.id)}) #サーバーID
dic.update({"guildName": message.guild.name}) #サーバー名
dic.update({"guildIcon": message.guild.icon}) #サーバーアイコン画像を示すキー
dic.update({"channelId": str(message.channel.id)}) #チャンネルID
dic.update({"channelName": message.channel.name}) #チャンネル名
dic.update({"messageId": str(message.id)}) #メッセージID
dic.update({"content": message.content}) #メッセージ内容
jsondata = json.dumps(dic, ensure_ascii=False) #辞書型リストをJSONに変換
await client.get_channel(json_channel_id).send(jsondata) #JSONチャンネルにJSONを送信
2021/09/02追記
※2021/09/01からスーパーグローバルチャットでは、ID類(messageId、userId、channelId、guildId、reference)が文字型(str型)になります。
スーパーグローバルチャットの規格策定時は、Discord.py(Python3)の仕様に合わせ、ID類はint型(JSONのNumber型)としていましたが、Discord.js(Node.js)では、Int型で扱うと下の桁が抜け落ちてしまい、それを防止するためにBigInt型で扱う事としていました。しかし、これでは標準ライブラリのJSONパッケージが使えないなどの不都合が合ったために、Str型に統一することとなりました。
当記事のサンプルプログラムでは、以前より問題無く作動するよう設計しておりました。
※Discord.py v2以降の場合
このままではエラーとなるため、以下のようにします。
if hasattr(message.author.avatar, 'key'): #アバター画像が設定されているとき
dic.update({"userAvatar": message.author.avatar.key})
else:
dic.update({"userAvatar": None})
if hasattr(message.guild.icon, 'key'): #アイコン画像が設定されているとき
dic.update({"guildIcon": message.guild.icon.key})
else:
dic.update({"guildIcon": None})
これで、添付ファイルや返信を除くメッセージのJSONデータが送信できるようになりました。
添付ファイルをJSONに追加
dic.update
の行の終わりとjsondata =
の行の間に、以下のものを追加します。
if message.attachments != []: #添付ファイルが存在するとき
arr = [] #リストを初期化
for attachment in message.attachments: #添付ファイルをループ
arr.append(attachment.proxy_url) #添付ファイルのURLを追加
dic.update({"attachmentsUrl": arr})
2021/09/02 追記
※2021/09/01から、スーパーグローバルチャットでは添付ファイルのパーセントエンコーディングが撤廃されます。
スーパーグローバルチャットの規格を作るときに、DiscordのメッセージでJSONの中にURLが入ることで、途中から全てリンクになってしまったため、それを解消するためにパーセントエンコーディングを導入しましたが、後からパーセントエンコーディングなどを行わなくても問題無く送受信できることを確認しました。
返信元メッセージIDをJSONに追加
スーパーグローバルチャットでは、返信元メッセージを、メッセージIDで指定します。
同じチャンネル内で返信している場合は、そのままメッセージIDを取得すれば良いのですが、他のチャンネルや他のBOTから受信したメッセージの返信の場合は少し複雑です。
そのために、あらかじめ埋め込みのフッターに差し込んであった「mID:」(大元のメッセージのID)を使用します。
if message.reference: #返信のとき
reference_msg = await message.channel.fetch_message(message.reference.message_id) #メッセージIDから、元のメッセージを取得
reference_mid = 0 #メンバーID用変数を初期化
if reference_msg.embeds and reference_msg.author == client.user: #返信の元のメッセージが、埋め込みメッセージかつ、このBOTが送信したメッセージのとき→グローバルチャットの他のサーバーからのメッセージと判断
arr = reference_msg.embeds[0].footer.text.split(" / ") #埋め込みのフッターを「 / 」区切りで取得
for ref_msg in arr: #区切ったフッターをループ
if "mID:" in ref_msg: #「mID:」が含まれるとき
reference_mid = ref_msg.replace("mID:","",1) #「mID:」を取り除いたものをメッセージIDとして取得
break
elif reference_msg.author != client.user: #返信の元のメッセージが、このBOTが送信したメッセージでは無い時→同じチャンネルのメッセージと判断
reference_mid = str(reference_msg.id) #返信元メッセージIDを取得
dic.update({"reference": reference_mid})
これで、JSONで返信元メッセージを伝達できるようになりました。
メッセージ送信部を辞書型リストに適応させる
JSONを実際に受信する前に、このままでは、JSONを受信したときに表示ができないので、修正していきます。
埋め込み本体
embed=discord.Embed(description=message.content, color=0x9B95C9) #埋め込みの説明に、メッセージを挿入し、埋め込みのカラーを紫`#9B95C9`に設定
embed=discord.Embed(description=dic["content"], color=0x9B95C9) #埋め込みの説明に、メッセージを挿入し、埋め込みのカラーを紫`#9B95C9`に設定
ユーザー情報
embed.set_author(name="{}#{}".format(message.author.name, message.author.discriminator),icon_url="https://media.discordapp.net/avatars/{}/{}.png?size=1024".format(message.author.id, message.author.avatar))
embed.set_author(name="{}#{}".format(dic["userName"], dic["userDiscriminator"]),icon_url="https://media.discordapp.net/avatars/{}/{}.png?size=1024".format(dic["userId"], dic["userAvatar"]))
フッター
embed.set_footer(text="{} / mID:{}".format(message.guild.name, message.id),icon_url="https://media.discordapp.net/icons/{}/{}.png?size=1024".format(message.guild.id, message.guild.icon))
if message.channel.name == global_channel_name:
bot_name = "このBOT"
else:
bot_name = message.author.name
embed.set_footer(text="{} / {} / mID:{}".format(dic["guildName"], bot_name, dic["messageId"]),icon_url="https://media.discordapp.net/icons/{}/{}.png?size=1024".format(dic["guildId"], dic["guildIcon"]))
このBOTで受信したメッセージなのか、他のBOTから受け取ったメッセージなのかを表示出来るように、「bot_name」を追加しています。
添付ファイル
if message.attachments != []: #添付ファイルが存在するとき
embed.set_image(url=message.attachments[0].url)
if "attachmentsUrl" in dic: #添付ファイルが存在するとき
embed.set_image(url=urllib.parse.unquote(dic["attachmentsUrl"][0]))
メッセージ返信
if message.channel.name == global_channel_name:
reference_msg = await message.channel.fetch_message(message.reference.message_id) #メッセージIDから、元のメッセージを取得
if reference_msg.embeds and reference_msg.author == client.user: #返信の元のメッセージが、埋め込みメッセージかつ、このBOTが送信したメッセージのとき→グローバルチャットの他のサーバーからのメッセージと判断
reference_message_content = reference_msg.embeds[0].description #メッセージの内容を埋め込みから取得
reference_message_author = reference_msg.embeds[0].author.name #メッセージのユーザーを埋め込みから取得
elif reference_msg.author != client.user: #返信の元のメッセージが、このBOTが送信したメッセージでは無い時→同じチャンネルのメッセージと判断
reference_message_content = reference_msg.content #メッセージの内容を取得
reference_message_author = reference_msg.author.name+'#'+reference_msg.author.discriminator #メッセージのユーザーを取得
if message.reference: #返信メッセージであるとき
if message.channel.name == global_channel_name:
reference_msg = await message.channel.fetch_message(message.reference.message_id) #メッセージIDから、元のメッセージを取得
if reference_msg.embeds and reference_msg.author == client.user: #返信の元のメッセージが、埋め込みメッセージかつ、このBOTが送信したメッセージのとき→グローバルチャットの他のサーバーからのメッセージと判断
reference_message_content = reference_msg.embeds[0].description #メッセージの内容を埋め込みから取得
reference_message_author = reference_msg.embeds[0].author.name #メッセージのユーザーを埋め込みから取得
elif reference_msg.author != client.user: #返信の元のメッセージが、このBOTが送信したメッセージでは無い時→同じチャンネルのメッセージと判断
reference_message_content = reference_msg.content #メッセージの内容を取得
reference_message_author = reference_msg.author.name+'#'+reference_msg.author.discriminator #メッセージのユーザーを取得
else: #JSONチャンネルから受信したとき
reference_mid = dic["reference"] #返信元メッセージID
reference_message_content = "" #返信元メッセージ用変数を初期化
reference_message_author = "" #返信元ユーザータグ用変数を初期化
past_dic = None #返信元メッセージの辞書型リスト用変数を初期化
async for past_message in message.channel.history(limit=1000): #JSONチャンネルの過去ログ1000件をループ
try: #JSONのエラーを監視
past_dic = json.loads(past_message.content) #過去ログのJSONを辞書型リストに変換
except json.decoder.JSONDecodeError as e: #JSON読み込みエラー→そもそもJSONでは無い可能性があるのでスルー
continue
if "type" in past_dic and past_dic["type"] != "message": #メッセージでは無い時はスルー
continue
if not "messageId" in past_dic: #キーにメッセージIDが存在しない時はスルー
continue
if str(past_dic["messageId"]) == str(reference_mid): #過去ログのメッセージIDが返信元メッセージIDと一致したとき
reference_message_author = "{}#{}".format(past_dic["userName"],past_dic["userDiscriminator"]) #ユーザータグを取得
reference_message_content = past_dic["content"] #メッセージ内容を取得
break
枠組みが一旦完成
これで、JSONメッセージを受け入れるための枠組みができました。
JSONを実際に受信する前に、この状態でBOT内でメッセージの送受信が問題無く行えるか確認しましょう。
ここまでのプログラムまとめ
import discord
import json
import urllib.parse
TOKEN = "<Token>" #トークンを入力
global_channel_name = "super-global-test" #設定したいチャンネル名を入力
json_channel_id = 878353900275642478 #JSONチャンネルID
client = discord.Client() #接続に必要なオブジェクトを生成
@client.event
async def on_message(message):
if message.channel.name == global_channel_name or message.channel.id == json_channel_id:#グローバルチャットかJSONチャンネルにメッセージが来たとき
#メッセージ受信部
if message.channel.name == global_channel_name: #グローバルチャットにメッセージが来たとき
if message.author.bot: #BOTの場合は何もせず終了
return
"""ここにJSONの送信部分を記述します"""
dic = {} #辞書型リストを初期化
dic.update({"type": "message"}) #JSONで送信するデータの種類: メッセージ
dic.update({"userId": str(message.author.id)}) #ユーザーID
dic.update({"userName": message.author.name}) #ユーザーネーム
dic.update({"userDiscriminator": message.author.discriminator}) #ユーザータグ
dic.update({"userAvatar": message.author.avatar}) #ユーザーのアバター画像を示すキー
dic.update({"isBot": message.author.bot}) #ユーザーがBOTかどうか
dic.update({"guildId": str(message.guild.id)}) #サーバーID
dic.update({"guildName": message.guild.name}) #サーバー名
dic.update({"guildIcon": message.guild.icon}) #サーバーアイコン画像を示すキー
dic.update({"channelId": str(message.channel.id)}) #チャンネルID
dic.update({"channelName": message.channel.name}) #チャンネル名
dic.update({"messageId": str(message.id)}) #メッセージID
dic.update({"content": message.content}) #メッセージ内容
if message.attachments != []: #添付ファイルが存在するとき
arr = [] #リストを初期化
for attachment in message.attachments: #添付ファイルをループ
arr.append(attachment.proxy_url) #添付ファイルのURLを追加
dic.update({"attachmentsUrl": arr})
if message.reference: #返信のとき
reference_msg = await message.channel.fetch_message(message.reference.message_id) #メッセージIDから、元のメッセージを取得
reference_mid = 0 #メンバーID用変数を初期化
if reference_msg.embeds and reference_msg.author == client.user: #返信の元のメッセージが、埋め込みメッセージかつ、このBOTが送信したメッセージのとき→グローバルチャットの他のサーバーからのメッセージと判断
arr = reference_msg.embeds[0].footer.text.split(" / ") #埋め込みのフッターを「 / 」区切りで取得
for ref_msg in arr: #区切ったフッターをループ
if "mID:" in ref_msg: #「mID:」が含まれるとき
reference_mid = ref_msg.replace("mID:","",1) #「mID:」を取り除いたものをメッセージIDとして取得
break
elif reference_msg.author != client.user: #返信の元のメッセージが、このBOTが送信したメッセージでは無い時→同じチャンネルのメッセージと判断
reference_mid = str(reference_msg.id) #返信元メッセージIDを取得
dic.update({"reference": reference_mid})
jsondata = json.dumps(dic, ensure_ascii=False) #辞書型リストをJSONに変換
await client.get_channel(json_channel_id).send(jsondata) #JSONチャンネルにJSONを送信
if message.channel.id == json_channel_id: #JSONチャンネルにメッセージが来たとき
if message.author == client.user: #メッセージ送信者がこのBOTの場合は何もせず終了
return
"""ここにJSONの受信部分を記述します"""
#メッセージ送信部
for channel in client.get_all_channels(): #BOTが所属する全てのチャンネルをループ
if channel.name == global_channel_name: #グローバルチャット用のチャンネルが見つかったとき
if channel == message.channel: #発言したチャンネルには送らない
continue
embed=discord.Embed(description=dic["content"], color=0x9B95C9) #埋め込みの説明に、メッセージを挿入し、埋め込みのカラーを紫`#9B95C9`に設定
embed.set_author(name="{}#{}".format(dic["userName"], dic["userDiscriminator"]),icon_url="https://media.discordapp.net/avatars/{}/{}.png?size=1024".format(dic["userId"], dic["userAvatar"]))
if message.channel.name == global_channel_name:
bot_name = "このBOT"
else:
bot_name = message.author.name
embed.set_footer(text="{} / {} / mID:{}".format(dic["guildName"], bot_name, dic["messageId"]),icon_url="https://media.discordapp.net/icons/{}/{}.png?size=1024".format(dic["guildId"], dic["guildIcon"]))
if "attachmentsUrl" in dic: #添付ファイルが存在するとき
embed.set_image(url=urllib.parse.unquote(dic["attachmentsUrl"][0]))
if message.reference: #返信メッセージであるとき
if message.channel.name == global_channel_name:
reference_msg = await message.channel.fetch_message(message.reference.message_id) #メッセージIDから、元のメッセージを取得
if reference_msg.embeds and reference_msg.author == client.user: #返信の元のメッセージが、埋め込みメッセージかつ、このBOTが送信したメッセージのとき→グローバルチャットの他のサーバーからのメッセージと判断
reference_message_content = reference_msg.embeds[0].description #メッセージの内容を埋め込みから取得
reference_message_author = reference_msg.embeds[0].author.name #メッセージのユーザーを埋め込みから取得
elif reference_msg.author != client.user: #返信の元のメッセージが、このBOTが送信したメッセージでは無い時→同じチャンネルのメッセージと判断
reference_message_content = reference_msg.content #メッセージの内容を取得
reference_message_author = reference_msg.author.name+'#'+reference_msg.author.discriminator #メッセージのユーザーを取得
else:
reference_mid = dic["reference"] #返信元メッセージID
reference_message_content = "" #返信元メッセージ用変数を初期化
reference_message_author = "" #返信元ユーザータグ用変数を初期化
past_dic = None #返信元メッセージの辞書型リスト用変数を初期化
async for past_message in message.channel.history(limit=1000): #JSONチャンネルの過去ログ1000件をループ
try: #JSONのエラーを監視
past_dic = json.loads(past_message.content) #過去ログのJSONを辞書型リストに変換
except json.decoder.JSONDecodeError as e: #JSON読み込みエラー→そもそもJSONでは無い可能性があるのでスルー
continue
if "type" in past_dic and past_dic["type"] != "message": #メッセージでは無い時はスルー
continue
if not "messageId" in past_dic: #キーにメッセージIDが存在しない時はスルー
continue
if str(past_dic["messageId"]) == str(reference_mid): #過去ログのメッセージIDが返信元メッセージIDと一致したとき
reference_message_author = "{}#{}".format(past_dic["userName"],past_dic["userDiscriminator"]) #ユーザータグを取得
reference_message_content = past_dic["content"] #メッセージ内容を取得
break
reference_content = ""
for string in reference_message_content.splitlines(): #埋め込みのメッセージを行で分割してループ
reference_content += "> " + string + "\n" #各行の先頭に`> `をつけて結合
reference_value = "**@{}**\n{}".format(reference_message_author, reference_content) #返信メッセージを生成
embed.add_field(name='返信しました', value=reference_value, inline=True) #埋め込みに返信メッセージを追加
await channel.send(embed=embed) #メッセージを送信
await message.add_reaction('✅') #リアクションを送信
client.run(TOKEN)
次はいよいよ、実際にJSONを受信します。
JSONを受信
"""ここにJSONの受信部分を記述します"""
try: #JSONのエラーを監視
dic = json.loads(message.content) #JSONを辞書型リストに変換
except json.decoder.JSONDecodeError as e: #JSON読み込みエラー→そもそもJSONでは無い可能性があるのでスルー
return
if "type" in dic and dic["type"] != "message": #メッセージ以外のJSON場合はスルー
return
if not "messageId" in dic: #キーに「"messageId"」がない場合
dic["messageId"] = str(message.id) #JSONメッセージ本体のメッセージIDを仮に挿入
初期のスーパーグローバルチャットでは、キーに「"messageId"」はありませんでした。
旧式のスーパーグローバルチャットの形式を使用するBOTも存在しますし、エラーにならないようにするために、ここでは仮にJSONメッセージのメッセージIDを、大元のメッセージIDの代わりにしています。
完成!!!
これで、スーパーグローバルチャットの導入に必要な条件が全て整いました。
問題無く動作するかを確認し、是非スーパーグローバルチャットにご参加ください。
参加のご連絡は@Tskikoh#0721
まで
スーパーグローバルチャットはこのサーバーで導入しております。
動作感の確認や参加以外のご連絡・質問等はこちらへどうぞ
The Global Chat's Guild