discordで、サーバーのメンバーに自由にチャンネルを作成する権限を解放したかったが、削除の権限も一緒に解放されてしまうため、うっかり違うチャンネルを削除してしまったとかが起こりそうで、なかなか権限解放に踏み切れなかった。
ということで、botを介してチャンネルの作成削除を行うことで、自分が作成したチャンネルじゃないと削除できないようにしてみた。
事前にチャンネルの作成、削除を行える役職を作成しておいて、それを対象のbotに割り当てる必要がある。
環境
- ubuntu 16.04.3 LTS + LXDE
- javascript
- node.js 9.4.0
- discord.js 11.3.0
チャンネルの作成
チャンネルの作成は、guildオブジェクト(1つのサーバーを表すオブジェクトらしい)が持っているcreateChannelメソッドで行うことができる。
client.on("message", (message) =>{message.guild.createChannel( 'sample', 'text' );}
とりあえずこれだけで、sampleという名前のテキストチャンネルが作成される。ボイスチャンネルを作りたい場合は、第二引数を'voice'にする。
また、特定のカテゴリの下にチャンネルを作りたい場合は、チャンネルを作った後に、そのチャンネルに親チャンネルを指定してあげればいい。
client.on("message", (message) =>{message.guild.createChannel( 'sample', 'text' ).then( (ch) => {// カテゴリもチャンネルの一種なので、channelsの中に入っているlet parent = message.guild.channels.find( 'name', 'Text Channels' );if ( parent ) {ch.setParent( parent );}}).catch( (err) => { console.log( err ); } );}
ちなみに、カテゴリ名に実際は小文字が使われていても、チャンネルリスト上では全部大文字で表示される。findメソッドで検索する時には、ちゃんと実際のカテゴリ名で検索しないと失敗する。
コマンドでチャンネル名を指定して作成する処理は、以下のような形にしてみた。うちのサーバーはカテゴリを使ってないので、単純にチャンネルを作成するだけになっている。
client.on("message", (message) =>{// コマンドとチャンネル名指定の引数にわけるlet arg = message.content.split( /\s+/ );const cmd = arg.shift();const ch_name = arg[0];if ( cmd === '!ch' ){// 既に同名のチャンネルが存在していないかチェック// 同名チャンネルも作成できるが、消すときに困るので同名は弾くif ( !message.guild.channels.exists( 'name', ch_name ) ){message.guild.createChannel( ch_name, 'text' ).then( (ch) => {ch.send( message.member.displayName + 'が作成しました' );}).catch( (err) => { console.log( err ); } );}}}
チャンネルの作成に成功したら、作成したチャンネルで作成者の名前を表示するようになっている。
thenとかcatchは、createChannelメソッドが返してくるpromisオブジェクトのメソッドで、非同期処理を扱うものらしい。
とりあえず、createChannelが終わるとthenの中身が実行される。失敗した場合はcatchの方が呼ばれる。
チャンネルの削除
チャンネルの削除は、channelオブジェクトのdeleteメソッドを呼ぶだけで削除される。
client.on("message", (message) =>{let ch = message.guild.channels.find( 'name', 'sample' );if ( ch ) {ch.delete();}}
コマンドで削除チャンネルを指定するのは、チャンネル作成と同じなので省略。
チャンネル作成者のみ削除を行えるようにする
作成したチャンネルのIDと作成者のIDを記録しておいて、削除の時に参照するようにする。
また、botを再起動しても大丈夫なように、チャンネル作成ログをJSONファイルとして出力するようにする。
以下がそのコード。
"use strict"// JSONファイル出力関数const write_json = ( filename, obj ) =>{fs.writeFile( filename, JSON.stringify( obj, null, '\t' ), (e) => {if ( e ) {console.log( e );throw e;}});}const Discord = require( 'discord.js' );const fs = require( 'fs' );const client = new Discord.Client();const token = "xxxxxxxx"; // トークンに置き換えconst ch_log_filename = 'ch_log.json';let ch_log ={};// ch_log.jsonが存在していれば読み込み、無ければchannels配列を作成try {const str = fs.readFileSync( ch_log_filename, 'utf8' );ch_log = JSON.parse( str );}catch ( err ) {ch_log.channels = new Array();}client.on("ready", () => {console.log("im ready test bot");});client.on("message", (message) =>{let arg = message.content.split( /\s+/ );const cmd = arg.shift();const ch_name = arg[0];if ( cmd === '!ch' ){if ( !message.guild.channels.exists( 'name', ch_name ) ){message.guild.createChannel( ch_name, 'text' ).then( (ch) => {ch.send( message.member.displayName + 'が作成しました' );let obj = {ch_id: ch.id,user_id: message.member.id,}// チャンネルIDとユーザーIDを追加してJSONファイルに出力ch_log.channels.push( obj );write_json( ch_log_filename, ch_log );}).catch( (err) => { console.log( err ); });}else {message.channel.send( '同名のチャンネルが既に存在しています' );}}if ( cmd === '!dch' ){let channel = message.guild.channels.find( 'name', ch_name );if ( channel ){const index = ch_log.channels.findIndex( (obj)=>{return obj.ch_id === channel.id;});// 削除しようとしているユーザーが作成者かチェックif ( message.member.id === ch_log.channels[index].user_id ){channel.delete().then( (ch) => {// 削除したチャンネルじゃなければ削除メッセージを送信if ( ch.id !== message.channel.id ) {message.channel.send( ch_name + 'チャンネルを削除しました' );}// 削除したチャンネルのログを削除してJSONファイルに出力ch_log.channels.splice( index, 1 );write_json( ch_log_filename, ch_log );}).catch( (err) => { console.log( err ); } );}else {message.channel.send( ch_name + 'チャンネルを削除する権限がありません' );}}else {message.channel.send( ch_name + 'チャンネルは存在しません' );}}});client.login(token);
そろそろコードブロック表示ちゃんと対応しないと厳しいな…。