先日 多言語Webニュースアプリ を作りました。 通勤電車で当日のニュースをチェックしながら、外国語を勉強できるので、まあまあ使いやすかったです。
実はこのWebを開発する1年ほど前に、一度Telegram botで多言語ニュースチャットボットを開発していました。今日はそれについて紹介したいと思います。
https://github.com/aibazhang/multitrue-bot 事前準備 Telegram bot Telegramは日本ではあまり人気がないですが、Telegramのアカウントを作るだけでAPIキーを取得できるので、ざくっとボット作りたい場合は使い勝手がけっこういいです。また、良さげのSDKもあります↓。そういう意味ではSlackも同じですが、Telegramは感覚的にLineに近いので手軽さで勝っていると思います。
https://github.com/python-telegram-bot/python-telegram-bot/tree/master/examples ニュースの取得 ニュースの取得方法は先日開発したWebアプリと同じく NewsAPI 利用して取得しています。なぜNewsAPIを採用したかについては 半年前の記事 で詳細を説明したので、割愛します。
実装 https://core.telegram.org/ に参照してボットの初期設定が完了したら、実装に入ります。 フローは以下となります。
/startでニュースボットを起動 国・地域を選ぶ ニュースのジャンルを選ぶ 終了あるいは2.に戻る 全体フロー SDKが提供してくれた下記3つのhandlerを利用しています。
ConversationHandler: 先ほど設計したフローに基づいてhandlerを定義する。 CommandHandler: コマンドによって発火される。今回は「入り口 (entry point)」として使う CallbackQueryHandler: 他のhandlerの返り値によって発火される def main(): updater = Updater( token=json.load(open(KEY_PATH / "keys.json", "r"))["telegram_key"], use_context=True, ) dispatcher = updater.dispatcher country_pattern = "^us|jp|cn|tw|kr|gb$" headlines_pattern = "^us|jp|cn|tw|kr|gb business|entertainment|general|health|science|sports|technology|$" conv_handler = ConversationHandler( entry_points=[CommandHandler("start", start)], states={ "CATEGORY": [CallbackQueryHandler(select_category, pattern=country_pattern)], "HEADLINES": [CallbackQueryHandler(get_news, pattern=headlines_pattern)], "START OVER OR NOT": [ CallbackQueryHandler(start_over, pattern="^start over$"), CallbackQueryHandler(end, pattern="^end$"), ], }, fallbacks=[CommandHandler("start", start)], ) dispatcher.add_handler(conv_handler) updater.start_polling() updater.idle() メニュー 続いてはcallback関数を見ていきましょう。start関数はwelcomeメッセージをユーザに送って、ユーザに国・地域を選んでもらいます。その後、select_category関数でニュースのジャンルを選んでもらいます。
引数contextとupdate
context.user_data: ボットを利用してユーザの情報を取得する context.bot: ボット自体を操作する(今回はメッセージを送るだけ)のに使う update: メッセージを更新するのに使う def start(update, context): user = update.message.from_user logger.info("User {} started the conversation.".format(user)) for i, v in vars(user).items(): context.user_data[i] = v welcome_message = ( "Hello, {}\n" "This is JC News bot🗞️🤖\n\n" "You can get Top News Headlines for a Country and a Category from here. \n\n".format(user.first_name) ) print(context) keyborad = [ [ InlineKeyboardButton("🇺🇸", callback_data="us"), InlineKeyboardButton("🇯🇵", callback_data="jp"), InlineKeyboardButton("🇹🇼", callback_data="tw"), ], [ InlineKeyboardButton("🇰🇷", callback_data="kr"), InlineKeyboardButton("🇬🇧", callback_data="gb"), InlineKeyboardButton("🇨🇳", callback_data="cn"), ], ] reply_markup = InlineKeyboardMarkup(keyborad) context.bot.send_message(chat_id=update.effective_chat.id, text=welcome_message) update.message.reply_text("Please Choose a Country🤖", reply_markup=reply_markup) return "CATEGORY" def select_category(update, context): logger.info("User data from context {}".format(context.user_data)) logger.info("Chat data from context {}".format(context.chat_data)) logger.info("Bot data from context {}".format(context.bot_data)) query = update.callback_query query.answer() country = query.data keyborad = [ [ InlineKeyboardButton("👩🏼💻Technology", callback_data=country + " technology"), InlineKeyboardButton("🧑💼Business", callback_data=country + " business"), ], [ InlineKeyboardButton("👨🏻🎤Entertainment", callback_data=country + " entertainment"), InlineKeyboardButton("👩🏻⚕️Health", callback_data=country + " health"), ], [ InlineKeyboardButton("👨🏿🔬Science", callback_data=country + " science"), InlineKeyboardButton("🏋🏼♂️Sports", callback_data=country + " sports"), ], [InlineKeyboardButton("🌎General", callback_data=country + " general")], ] reply_markup = InlineKeyboardMarkup(keyborad) query.edit_message_text(text="Please Choose a Category🤖", reply_markup=reply_markup) return "HEADLINES" ニュースを取得 最後は最もコアとなるニュースを取得するロジックです。countryとcategoryによってNewAPIから最新ホットラインニュースを取得しフォーマットした後、ユーザにメッセージを送ります。 具体的な詳細は説明しませんが、NewsAPICollectorというクラスでNewAPIをラッピングしています。
...