LoginSignup
22
5

More than 1 year has passed since last update.

あなたの知らないディズニーキャラクターの世界

Last updated at Posted at 2021-10-19

君の名は

※今回、投稿主の重度のヲタクぶりによりDヲタ1用語が頻出しています。注釈をつけていますのでご活用ください。

突然ですが、投稿主はこう見えて大のディズニー好きでございます。
アイコンも実はミニーちゃんカチューシャです。
現在のようにチケット入手が困難になる以前は月に1回はパーク2に行っていました。
(ディズニー好きにしては少ない方です。)
そんなディズニー大好きな私ですが、パークでフリグリ3に遭遇したとき
「あれ?あのキャラクターなんていう名前だっけ……?」となるときがあります。
パークを訪れているとき以外でも、イラストで見たとき、金曜ロードショーに登場したとき、などなど……。
なぜか思い出せないのです。こんなにディズニー愛に溢れているというのに……!

突然やってくる「あれ?なんだっけ?」のために、普段からディズニーキャラクターについて学んでおく必要があるのです。
そしていつの日か「なんでそんな脇役キャラクターの名前まで知ってるの?!」
と驚いてもらえるように、あなたも一日一回ディズニーキャラクターを覚えていきましょう!
どうやって覚えればいいのかって?
安心して!一言問いかけてもらえれば魅力あふれるディズニーキャラクターたちを勝手に教えるから!

夢を求め続ける勇気さえあれば、すべての夢は必ず実現できる。
いつだって忘れないでほしい。
すべて一匹のねずみから始まったということを。
ーウォルト・ディズニー

概要

Web APIを使ったオリジナルLINE Botの作成を行います。
普段からディズニーキャラクターに触れてほしいため、一番身近なアプリ「LINE」を使って、
ランダムにキャラクター情報を取得できるようにします。

  • LINE Botを使用します。
  • ユーザーには「キャラクター」と呼びかけてもらうようにします。
  • DisneyAPIを呼び出し、ランダムでキャラクター情報を取得します。
  • 取得したキャラクターの名前と画像をLINEのトーク画面に返します。

環境

Node v16.10.0
axios 0.22.0
ngrok 2.3.40

コード

完成までの過程は感想から下に記載しています。
チャネルシークレット、チャネルアクセストークンは伏せています。

characters.js
'use strict';

// ########################################
//               初期設定など
// ########################################

// パッケージを使用します
const express = require('express');
const line = require('@line/bot-sdk');
const axios = require('axios');

// ローカル(自分のPC)でサーバーを公開するときのポート番号です
const PORT = process.env.PORT || 3000;

// Messaging APIで利用するクレデンシャル(秘匿情報)です。
const config = {
    channelSecret: '発行されたチャネルシークレット',
    channelAccessToken: '発行されたチャネルアクセストークン'
};

// ########## ▼▼▼ DisneyAPIからキャラクター情報を取得する関数 ▼▼▼ ##########
const charactersFunction = async (event) => {
    // ユーザーメッセージが「キャラクター」かどうか
    if (event.message.text !== 'キャラクター') {
        return client.replyMessage(event.replyToken, {
            type: 'text',
            text: 'チクタクチクタク(「キャラクター」と話しかけてね)'
        });
    } else {
        // 「リプライ」を使って先に返事しておきます
        await client.replyMessage(event.replyToken, {
            type: 'text',
            text: 'チクタクチクタク……(調べています……)'
        });

        let pushText = '';
        let pushImage = '';

        try {
            //キャラクターの種類
            const chara_count = 7450;
            //乱数(0以上1未満の小数)* 種類数 をして小数以下切り捨て
            const index = Math.floor(Math.random() * chara_count);
            //axiosでDisneyAPIを叩きランダムにキャラクター情報を取得(少し時間がかかる・ブロッキングする)
            let res = await axios.get("https://api.disneyapi.dev/characters/" + index);
            //キャラクターの名前を取得
            const characters = res.data.name;
            //キャラクターの画像URLを取得
            const charapic = res.data.imageUrl;
            //返信用のテキストと画像URLを作成
            pushText = characters;
            pushImage = charapic;
        } catch (error) {
            pushText = 'チクタクチクタク(フック船長がイタズラしたみたい!ごめんね!)';
            // APIからエラーが返ってきたらターミナルに表示する
            console.error(error);
        }

        if (pushText !== ''){
            //画像を先に返す
            await client.pushMessage(event.source.userId, {
            type: 'image',
            originalContentUrl: pushImage,
            previewImageUrl: pushImage
            });

            //名前
            return client.pushMessage(event.source.userId, {
            type: 'text',
            text: pushText
            });
        } else {
            //キャラクターが取得できなかった時のメッセージ
            pushText = 'チクタクチクタク(フック船長がイタズラしたみたい!ごめんね!)';
        }

    }
};
// ########## ▲▲▲ 関数 ▲▲▲ ##########

// ########################################
//  LINEサーバーからのWebhookデータを処理する部分
// ########################################

// LINE SDKを初期化します
const client = new line.Client(config);

// LINEサーバーからWebhookがあると「サーバー部分」から以下の "handleEvent" という関数が呼び出されます
async function handleEvent(event) {
    // 受信したWebhookが「テキストメッセージ以外」であればnullを返すことで無視します
    if (event.type !== 'message' || event.message.type !== 'text') {
        return Promise.resolve(null);
    }
    // 関数を実行します
    return charactersFunction(event);
}

// ########################################
//          Expressによるサーバー部分
// ########################################

// expressを初期化します
const app = express();

// HTTP POSTによって '/webhook' のパスにアクセスがあったら、POSTされた内容に応じて様々な処理をします
app.post('/webhook', line.middleware(config), (req, res) => {

  // 検証ボタンをクリックしたときに飛んできたWebhookを受信したときのみ以下のif文内を実行
  if (req.body.events.length === 0) {
    res.send('Hello LINE BOT! (HTTP POST)'); // LINEサーバーに返答します(なくてもよい)
    console.log('検証イベントを受信しました!'); // ターミナルに表示します
    return; // これより下は実行されません
  } else {
    // 通常のメッセージなど … Webhookの中身を確認用にターミナルに表示します
    console.log('受信しました:', req.body.events);
  }

  // あらかじめ宣言しておいた "handleEvent" 関数にWebhookの中身を渡して処理してもらい、
  // 関数から戻ってきたデータをそのままLINEサーバーに「レスポンス」として返します
  Promise.all(req.body.events.map(handleEvent)).then((result) => res.json(result));
});

// 最初に決めたポート番号でサーバーをPC内だけに公開します
// (環境によってはローカルネットワーク内にも公開されます)
app.listen(PORT);
console.log(`ポート${PORT}番でExpressサーバーを実行中です…`);

結果

LINEで試した結果がこちらです。
LINEbot.gif

解説

Botのワニは、とある絵しりとりで書いた私の力作です。
どうせワニなら、とピーターパンに出てくるチクタクワニ4ということにしてみました。
びっくりするほど似てませんがかわいいので問題ありません。

ユーザーからのキーワードは「キャラクター」のみにしています。
そのため「キャラクター」以外の文字列を入力すると、Bot側がキャラクターと入力するよう促すメッセージを表示します。
検索にあまり時間はかかりませんが、先に調べていることをお知らせするメッセージも表示しています。
キャラクターが取得できたら画像とキャラクター名を表示します。

取得できなかった場合は、「フック船長がイタズラしたみたい!ごめんね!」と表示するメッセージも用意しました。
何回かテストしましたが、未取得とならなかったためこちらの結果表示はありません。ご了承ください。

ディズニーのアニメ映画以外にも、ディズニーチャンネルオリジナルアニメ、同チャンネルのオリジナルドラマ、実写映画、アトラクション、コミック、などなどディズニー関連のキャラクターであればなんでも返ってくるので、知らないキャラクターを教えてくれることの方が多いです。
念のため、取得したディズニーキャラクターの紹介を書いておきます。

  • 1人目:ゼノン・カー
    ディズニーチャンネルオリジナル映画「未来少女ゼノン:21世紀の少女」(ゼノンシリーズ第一作)の主人公。

  • 2人目:カーク・マッカリー
    ディズニーのアニメシリーズ「TheWeekenders」のキャラクター。

  • 3人目:グルミオ
    実写ミュージカルクリスマス映画「Babes in Toyland」のキャラクター。

  • 4人目:ロリー
    ディズニージュニア5のアニメ「Puppy Dog Pals」のキャラクター。

  • 5人目:ファ・リー
    ディズニー映画「ムーラン」のキャラクター。ムーランの母親。

  • 6人目:ディキシー
    ディズニー映画「きつねと猟犬2 トッドとコッパーの大冒険」に登場するキャラクター。

  • 7人目:チェルシー・ブリマー
    ディズニーチャンネルオリジナルドラマ「The Suite Life」シリーズのキャラクター。

  • 8人目:ジム
    ディズニー映画「わんわん物語」のキャラクター。主人公レディの飼い主。

※Google翻訳にかけてキャラクター名をカタカナ表記にしたものもあるため正しくない場合があります。
各キャラクターの詳細情報、出演作についてはぜひ調べてみてください!

感想

お前誰だよ!っていうキャラクターばかり返ってきますが、ひとまず大成功です。
奥深きディズニーキャラクターの世界……。
知らないキャラクターの名前をたくさん知れて、自分で作っておきながらとても勉強になりました。
今回調べた8人は忘れられないかもしれません。

以下は反省点ともっとこうしたかった!という点です。
・キャラクー総数を直書きにした
確認したデータ数とページ数から取得可能キャラクター総数を計算しコードに直書きしました。
が、コード内で総数を取得し設定する方法にしたかったです。

・日本語名を返したい
英語表記だとわかりづらく、いちいち検索しなおす必要がありました。

・画像だけ先に出してクイズ形式にしたかった
キャラクター名を英語表記でしか取得できなかったため高難易度クイズになってしまい断念しました。

・画像を投稿したらキャラクター名を返すbotにしたかった。
投稿された画像を受け取りGoogleの画像検索にかけ、結果を返す、と頭に浮かびましたが力量不足で実装できませんでした。

・常に使えるbotにしたい
ngrokのアカウント無しかつ無料版では2時間という時間制限があります。
調べてみるとサインアップすると制限がなくなる、という記事がありました。
(参考記事:ngrokを無料プランでURL固定してみる
サインアップまではできたのですが、その先がわからずそのままになってしまいました。

色々と悔しい部分が残りましたが、今できる精一杯を形にしました。
前回に引き続き「動けばいい!」精神です。
残りは時間のある時に調べてみようと思います。

ディズニーランドが完成することはない。
世の中に想像力がある限り進化し続けるだろう。
ーウォルト・ディズニー

臥薪嘗胆

今回も完成コードができるまでをこちらに記載していきます。
例のごとくお時間のある方のみご覧ください。

まずは特定のキャラクター情報を取得してみる

手始めに特定のキャラクターの情報のみ表示できるようにしてみます。
ターゲットにしたキャラクターはディズニーシーでは一番新しいダフフレ6の「オル・メル」にします。
LINE Botで試す前にログに出力してみます。

const axios = require("axios");

//オルメルの情報を取得
async function main() {

    let response = await axios.get("https://api.disneyapi.dev/characters/6");
    console.log(response.data);
}

main();

結果はこちら

 _id: 6,
  shortFilms: [],
  tvShows: [],
  videoGames: [],
  parkAttractions: [],
  allies: [],
  enemies: [],
  sourceUrl: 'https://disney.fandom.com/wiki/%27Olu_Mel',      
  name: "'Olu Mel",
  imageUrl: 'https://static.wikia.nocookie.net/disney/images/6/61/Olu_main.png/revision/latest?cb=20200630025227',            61/Olu_main.png/revision/latest?cb=20200630025227',
  createdAt: '2021-04-12T01:25:09.759Z',
  updatedAt: '2021-04-12T01:25:09.759Z',
  url: 'https://api.disneyapi.dev/characters/6',
  __v: 0
}

成功です!
ここからまずは名前だけをLINE botで返せるようにしてみます。
以下はDisneyAPIからオル・メルの名前だけLINEで返す部分を抜粋しています。

// ########## ▼▼▼ DisneyAPIからキャラクター情報を取得する関数 ▼▼▼ ##########
const charactersFunction = async (event) => {
    // ユーザーメッセージが「キャラクター」かどうか
    if (event.message.text !== 'キャラクター') {
        return client.replyMessage(event.replyToken, {
            type: 'text',
            text: 'チクタクチクタク(「キャラクター」と話しかけてね)'
        });
    } else {
        // 「リプライ」を使って先に返事しておきます
        await client.replyMessage(event.replyToken, {
            type: 'text',
            text: 'チクタクチクタク……(調べています……)'
        });

        let pushText = '';
        try {
            // axiosでDisneyAPIを叩きます(少し時間がかかる・ブロッキングする)
            const res = await axios.get('https://api.disneyapi.dev/characters/6');
            // キャラクターを取得
            const characters = res.data.name;
            //返信用のテキストを作成
            pushText = characters;
        } catch (error) {
            pushText = 'チクタクチクタク(フック船長がイタズラしたみたい!ごめんね!)';
            // APIからエラーが返ってきたらターミナルに表示する
            console.error(error);
        }

        // 「プッシュ」で後からユーザーに通知します
        return client.pushMessage(event.source.userId, {
            type: 'text',
            text: pushText,
        });
    }
};

結果はこちら(LINE画面の抜粋です。)
image.png
成功です!
しっかりオル・メルの名前を返してくれました。
ユーザーが「キャラクター」と入力していないときは、「キャラクター」と入力するよう促す部分もうまくいっています。

キャラクターの画像を取得する

名前と一緒に画像も返すようにしてみます。
先ほどの名前を取得した下に imageUrlを入れて、画像のURLを取得します。
画像メッセージはタイプが imageなので、メッセージで送る際のタイプにも気をつけます。
originalContentUrlpreviewImageUrlを設定する必要があるみたいなので、こちらも設定します。
(参考:LINE 画像メッセージ

// ########## ▼▼▼ DisneyAPIからキャラクター情報を取得する関数 ▼▼▼ ##########
const charactersFunction = async (event) => {
    // ユーザーメッセージが「キャラクター」かどうか
    if (event.message.text !== 'キャラクター') {
        return client.replyMessage(event.replyToken, {
            type: 'text',
            text: 'チクタクチクタク(「キャラクター」と話しかけてね)'
        });
    } else {
        // 「リプライ」を使って先に返事しておきます
        await client.replyMessage(event.replyToken, {
            type: 'text',
            text: 'チクタクチクタク……(調べています……)'
        });

        let pushText = '';
        let pushImage = '';

        try {
            //axiosでDisneyAPIを叩きます(少し時間がかかる・ブロッキングする)
            const res = await axios.get('https://api.disneyapi.dev/characters/6');
            //キャラクターの名前を取得
            const characters = res.data.name;
            //キャラクターの画像URLを取得
            const charapic = res.data.imageUrl;
            //返信用のテキストと画像URLを作成
            pushText = characters;
            pushImage = charapic;
        } catch (error) {
            pushText = 'チクタクチクタク(フック船長がイタズラしたみたい!ごめんね!)';
            // APIからエラーが返ってきたらターミナルに表示する
            console.error(error);
        }

        // 「プッシュ」で後からユーザーに通知します
        return client.pushMessage(event.source.userId, {
            type: 'image',
            originalContentUrl: pushImage,
            previewImageUrl: pushImage,
            type: 'text',
            text: pushText,
        });
    }
};

結果はこちら。
image.png
かわいいー!(むふー!)
あれ?でも、名前が消えてしまった!これは失敗です。
画像の下に名前を表示したかったのですが、名前が表示されていません。

名前と画像どちらも表示させる

こちらのQiita記事を参考に、テキストと画像を返すところを変更してみます。
(参考記事:ジェイソン・ステイサムで妄想するのが日課になっていたので、いっそBOTにしてみた。

        await client.pushMessage(event.source.userId, {
            type: 'image',
            originalContentUrl: pushImage,
            previewImageUrl: pushImage
        });

        //名前
        return client.pushMessage(event.source.userId, {
            type: 'text',
            text: pushText
        });

結果はこちら。
image.png
成功です!
ウクレレを持ったハワイ生まれの亀の男の子の画像と、オル・メルという名前が表示されました!

次に大きな壁です。
このキャラクター情報をランダムに取得してみます……!

キャラクター情報をランダムに取得する

またまた原点に戻り、ランダムにキャラクター情報だけをとってくることをやってみます。
キャラクター数は https://api.disneyapi.dev/charactersをブラウザ表示させた際に、
フッター情報に
count: 50
totalPages: 149
とあったので、50×149=7450 とします。
直書きはイマイチですが、とりあえずは動くことを優先します。
エラー処理も用意してあるのでよしとします!
乱数計算については、とある資料を参考にしました。

const axios = require("axios");

//キャラクター情報を取得
async function main() {

    //キャラクターの種類
    const chara_count = 7450;
    // 乱数(0以上1未満の小数)* 種類数 をして小数以下切り捨て
    const index = Math.floor(Math.random() * chara_count);

    let response = await axios.get("https://api.disneyapi.dev/characters/" + index);

    var charaname = response.data.name;
    var charapic = response.data.imageUrl;
    console.log(charaname);
    console.log(charapic);
}

main();

算出した乱数をURLの末尾につけ、キャラクター情報を取得します。
何回か実行してみた結果がこちら。

Cassie
https://static.wikia.nocookie.net/disney/images/d/d5/CassieLivandMad.jpg/revision/latest?cb=20190825122326`
The Sinister 11
https://static.wikia.nocookie.net/disney/images/c/cc/Sinister_11.jpg/revision/latest?cb=20200928215241
Mandy
https://static.wikia.nocookie.net/disney/images/e/e6/Aliki_Mandy.jpg/revision/latest?cb=20120608073130

成功です!
キャラクター名が英語表記なのでピンときませんが、
名前と画像URLが取得できています。
では、これをLINE Botのほうに実装してみます。

いよいよLINE Bot

先ほどの、ユーザーからの呼びかけ内容を確認し、DisneyAPIを叩いてキャラクター情報を取得するところに、ランダムで取得する部分を入れてみます。

// ########## ▼▼▼ DisneyAPIからキャラクター情報を取得する関数 ▼▼▼ ##########
const charactersFunction = async (event) => {
    // ユーザーメッセージが「キャラクター」かどうか
    if (event.message.text !== 'キャラクター') {
        return client.replyMessage(event.replyToken, {
            type: 'text',
            text: 'チクタクチクタク(「キャラクター」と話しかけてね)'
        });
    } else {
        // 「リプライ」を使って先に返事しておきます
        await client.replyMessage(event.replyToken, {
            type: 'text',
            text: 'チクタクチクタク……(調べています……)'
        });

        let pushText = '';
        let pushImage = '';

        try {
            //キャラクターの種類
            const chara_count = 7450;
            //乱数(0以上1未満の小数)* 種類数 をして小数以下切り捨て
            const index = Math.floor(Math.random() * chara_count);
            //axiosでDisneyAPIを叩きランダムにキャラクター情報を取得(少し時間がかかる・ブロッキングする)
            let res = await axios.get("https://api.disneyapi.dev/characters/" + index);
            //キャラクターの名前を取得
            const characters = res.data.name;
            //キャラクターの画像URLを取得
            const charapic = res.data.imageUrl;
            //返信用のテキストと画像URLを作成
            pushText = characters;
            pushImage = charapic;
        } catch (error) {
            pushText = 'チクタクチクタク(フック船長がイタズラしたみたい!ごめんね!)';
            // APIからエラーが返ってきたらターミナルに表示する
            console.error(error);
        }

        //画像を先に返す
        await client.pushMessage(event.source.userId, {
            type: 'image',
            originalContentUrl: pushImage,
            previewImageUrl: pushImage
        });

        //名前
        return client.pushMessage(event.source.userId, {
            type: 'text',
            text: pushText
        });
    }
};

結果はこちら。
image.png
成功です!

……誰だ!お前!!
1枚目はピグレット7ではなく、後ろにいる犬さんが「Skippy(スキッピー)」だそうです。
プーさんの友達クリストファー・ロビン8の飼い犬とのこと。
2枚目はおそらくですが「クワイベリングダック」
コミック「CrisisonInfiniteDarkwings」に登場するダークウィングダック9の変形バージョンのようです。
奥深いぜ、ディズニーキャラクター……!!

キャラクターが取得できなかったとき

何度か試しているとメッセージを返してこないときがありました。
image.png
実際のキャラクター数よりも多い数で乱数計算しているので返ってこないのかもしれません。
キャラクターが取得できなかったらメッセージを返すようにしてみます。
if文を追加したところだけ抜粋します。

        if (pushText !== ''){
            //画像を先に返す
            await client.pushMessage(event.source.userId, {
            type: 'image',
            originalContentUrl: pushImage,
            previewImageUrl: pushImage
            });

            //名前
            return client.pushMessage(event.source.userId, {
            type: 'text',
            text: pushText
            });
        } else {
            //キャラクターが取得できなかった時のメッセージ
            pushText = 'チクタクチクタク(フック船長がイタズラしたみたい!ごめんね!)';
        }

試してみたところ、なかなかキャラクター未取得にはならずうまくいったのかどうかが分かりませんでした……。
時々返ってくるのが遅いこともあるみたいなので、返事がないのはそのタイミングにはまっているときなのかもしれません。
それにしても知らないディズニーキャラクターばかりで勉強になりました。
とはいえ出てくる結果の大半が……
誰だよ!!お前!!

参考

▼キャラクター情報はこちらから取得しました
The Disney Wiki
▼キャラクター情報の補足として活用しました
ウィキペディア


  1. ディズニーヲタクのこと。(≒舞浜ヲタ) 

  2. ディズニーランド、またはディズニーシーのこと。ディズニー好きはパークと呼んでいる。(例「今日のパークは混んでるね」) 

  3. フリーグリーティングの略。場所や時間に決まりがなく神出鬼没なグリーティング10のことをいう。混雑日は接触困難である。なお、現在は一定の距離を保ってのグリーティングとなっている。(例「週末のフリグリは無理ゲー」) 

  4. ピーターパンに出てくるワニ。時計を飲み込んだため近づいてくると「チクタク」と音が聞こえる。ウェンディたちがネバーランドにやってくるよりも前、ピーターパンがフック船長の左手を切り落としこのワニに与えた。(結構ひどい)その結果味をしめたチクタクワニはフック船長につきまとい隙あらば残りの部分も捕食しようとしている。パークでフック船長に会ったときに近くで「チクタク」と声をかけるとおびえる。 

  5. ディズニーが運営している子供向け番組の専門チャンネル 

  6. ダッフィー&フレンズの略。詳しくはこちら 

  7. 「くまのプーさん」に出てくる子豚のキャラクター。プーさんの親友で臆病な性格。ハムを食べるシーンがあるが子豚の人形なので共食いにはならない。 

  8. 「くまのプーさん」に出てくる男の子。プーの親友。森のはずれの高台に住んでいる少年。字の読み書きができ、いつも名案を思いつくので森の仲間たちからたよりにされている。原作の作者A.A.ミルンの息子がモデルと言われている。 

  9. アメリカで放映されていたアニメ「ダックにおまかせ ダークウィング・ダック」の主人公。普段はただの親父だが、影のヒーロー「ダークウィング・ダック」として数々の秘密兵器や機転・仲間の助け・そして運を武器に戦う。 

  10. キャラクターと会うこと。常設施設があったり、突如はじまるものもある。特定の日(ミッキーの誕生日など)の場合特別に整理券が発行されることもある。「グリ」と略されることが多い。(例「今日はグリ目的で来ました。」) 

22
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
5