#elixirlang 언어로 #telegram 봇 만들기
텔레그램은 훌륭한 메신저이자 클라이언트다. 훌륭한 클라이언트가 된 건 bot api 덕분이다. 간단한 iOS 앱을 만들려고 했는데, 텍스트로 제어가 충분하다고 생각하니 텔레그램 봇으로 만들면 되겠단 생각이 들었다. 혼자 쓸 거라 만들어 본 슬랙(slack) 봇은 과하다.
bot api를 쌩으로 다 호출하기는 괴로우니 텔레그램 bot api wrapper 패키지를 찾아봤다. 다운로드 수가 가장 많은 nadia를 사용하기로했다. 설명이 부실해 이리저리 헤맸다. 다행히 elixir-telegram-bot-boilerplate 프로젝트를 찾아서 시간을 절약했다.
텔레그램에서 봇 계정을 만들고 토큰을 발급받아야 한다. godfather가 아닌 botfather라니 센스 죽인다. 웹 페이지를 기대했는데, 봇을 통해 봇 계정을 만들다니. 봇은 이렇게 쓰는 거에요. 이런 설명도 따로 필요 없겠더라.
$ mix new ping --sup
프로젝트를 만든다.
use Mix.Config
config :nadia,
token: "BOT TOKEN"
발급받은 토큰을 config.exs
파일에 설정으로 추가한다.
defmodule Ping.MixProject do
use Mix.Project
# ...
def application do
[
extra_applications: [:logger, :nadia],
mod: {Ping.Application, []}
]
end
defp deps do
[
{:nadia, "~> 0.4.4"}
]
end
end
mix.exs
파일에 의존 라이브러리와 시작 애플리케이션을 설정한다.
defmodule Ping.Poller do
use GenServer
require Logger
@interval_ms 1000
def start_link(args) do
GenServer.start_link(__MODULE__, args, name: __MODULE__)
end
def init(_args) do
schedule_update()
{:ok, nil}
end
def handle_info(:update, offset) do
schedule_update()
ret =
if offset do
Nadia.get_updates(offset: offset)
else
Nadia.get_updates()
end
new_offset = ret |> process_messages
{:noreply, new_offset + 1}
end
defp schedule_update() do
Process.send_after(self(), :update, @interval_ms)
end
defp process_messages({:ok, []}), do: -1
defp process_messages({:ok, results}) do
results
|> Enum.map(fn %{update_id: id} = message ->
message
|> process_message
id
end)
|> List.last()
end
defp process_message({:error, error}) do
Logger.warn("error - #{inspect(error)}")
end
defp process_message(message) do
msg = message.message
if msg do
text = msg.text
chat_id = msg.chat.id
message_id = msg.message_id
if String.trim(text) == "/ping" do
Nadia.send_message(chat_id, "**pong**\n`pong`\n_pong_```\npong\n```",
reply_to_message_id: message_id,
parse_mode: "Markdown"
)
end
end
end
end
Ping.Poller
모듈을 lib/ping/poller.ex
파일에 만든다. 1초마다 Nadia.get_updates/1
함수를 불러서 메시지를 가져온다. offset
값을 관리해야 메시지를 중복으로 가져오는 일이 없다. 응답으로 받은 update_id
값에 1을 더해서 요청하면 된다. 메시지에 markdown을 사용할 수 있어서 좀 더 풍부한 응답을 보낼 수 있다.
defmodule Ping.Application do
use Application
def start(_type, _args) do
children = [
{Ping.Poller, []}
]
opts = [strategy: :one_for_one, name: Ping.Supervisor]
Supervisor.start_link(children, opts)
end
end
슈퍼바이저를 만들어 Ping.Poller
모듈에서 크래시가 났을 때, 다시 실행하게 한다.
$ mix do deps.get, run --no-halt
의존 라이브러리를 받고 텔레그램 봇을 실행. 이제 만든 텔레그램 봇 계정과 대화를 시작한다. /ping
명령을 입력해본다.