작년에 클라이언트 프로그램에서 이슈가 발생했을 때 사용자에게 문자를 보내줘야하는 기능을 개발 해야했는데
SMS는 돈주고 사야하므로 유료이므로 난 그럴 능력이 없으므로 텔레그램 봇을 개발했었다.
그때는 "NetTelegramBotApi"라는 라이브러리를 사용해 개발을 했었는데
지금 글을 쓰려고 다시 찾아보니 Nuget에 더 좋은 라이브러리가 있었다.
"NetTelegramBotApi"는 패스
Nuget에서 telegram을 검색하면 제일 위에 나오는 "Telegram.Bot"라이브러리를 써보자.
<- 요놈 받으면 된다.
일단 개발해보기에 앞서 Telegram Bot을 만들려면 API access 키를 발급 받아야 한다.
어려울거 하나 없다.
핸드폰 보랴 모니터 보랴 왔다갔다 하지말고 https://web.telegram.org/ 에 들어가서 봇을 추가하자.
1. BotFather를 찾아라.
BotFather를 클릭하면 아래와 같이 화면이 바뀐다.
Start 버튼을 클릭하자.
그럼, 봇아빠가 알아듣는 커맨드가 쭈루룩 나온다. 그걸 잘 읽어보면 봇을 만들 수 있다.
2. Bot을 추가해라.
봇아빠에게 /newbot 이라고 입력하자.
그럼 이름을 말하라고 한다.
원하는 봇의 이름을 입력하자.
주의할점은 끝에 _bot 또는 Bot이 꼭 들어가야 한다. 안그러면 안만들어쥼
이름을 잘 입력해서 만들면 access token을 바로 똭하고 준다.
/// 그 외 다양한 커맨드가 있으니 한번씩 해보세요. 재밌어요.
3. Access Token을 받아 개발을 해보자.
그럼 access token을 복사해서 봇을 만들어 보자.
* 참고
Telegram 홈페이지 Telegram Bot API : Telegram API Doc
Telegram.Bot 라이브러리 도큐먼트 : Telegram.Bot dll Doc
Telegram.Bot 라이브러리 예제 : Telegram.Bot Example << 예제를 보면 다양한 케이스가 많다. 사진, 음성, 위치접근, 연락처, 등등
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////만들어 보자
나는 C# 콘솔 응용 프로그램 으로 만들었다.
class Program
{
static void Main(string[] args)
{
/// 봇 접근 함수 호출
testAPIAsync();
Console.ReadLine();
}
/// 비동기 봇 접근
static async void testAPIAsync()
{
var Bot = new Telegram.Bot.TelegramBotClient("봇 아빠가 준 access token을 넣어요.");
var me = await Bot.GetMeAsync();
// 올바르게 접속이 되면 봇 이름이 출력된다.
System.Console.WriteLine("Hello my name is " + me.FirstName);
}
}
위 소스를 실행 하면 다음과 같은 결과 화면을 볼 수 있다.
간단하게 봇을 실행 시키는 것까지 성공했다.
하지만 내가 해야할 것은 이벤트가 생겼을 때 telegram 봇이 사용자에게 메세지를 전송 하거나,
사용자로 부터 명령을 받아 DB를 바꾼다던지, DB를 조회해 정보를 보여준다던지 등의 작업을 해야한다.
아래 소스를 하나씩 따라가보자.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
///텔레그램 dll using
using Telegram.Bot;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
using Telegram.Bot.Types.ReplyMarkups;
namespace telegram
{
class Program
{
//봇 생성
private static readonly TelegramBotClient Bot = new TelegramBotClient("봇 아빠가 준 access token을 넣어요.");
//임시방편 유저 리스트
private static List<User> Users = new List<User>();
//telegram 사용자의 상태 저장 변수
private static Dictionary<long, UserState> dicUserState = new Dictionary<long, UserState>();
static void Main(string[] args)
{
///임시방편 유저 리스트에 유저 추가
Users.Add(new User("시나공공", 28)); //28살이다...ㅁㄴ아ㅓㄹ ㅓㅁㄴ아ㅣㄹ허 님
Users.Add(new User("텔레그램", 30));
Users.Add(new User("봇아빠", 55));
///봇 이벤트 추가
Bot.OnMessage += Bot_OnMessage;
Bot.OnMessageEdited += Bot_OnMessage;
Bot.OnReceiveError += Bot_OnReceiveError;
///Me 획득 (Me가 뭔지 정확하게는.... 일단 눈치껏 보면 Username에 sinagonggongBot이 들어가는걸 보면 Bot 본인을 뜻하는 듯하다)
var me = Bot.GetMeAsync().Result;
Console.Title = me.Username;
/// Recv Start
Bot.StartReceiving();
Console.ReadLine();
/// Recv Stop
Bot.StopReceiving();
}
/// Recv Error
private static void Bot_OnReceiveError(object sender, Telegram.Bot.Args.ReceiveErrorEventArgs e)
{
Debugger.Break();
}
///사용자로 부터 Message Recv
private static async void Bot_OnMessage(object sender, Telegram.Bot.Args.MessageEventArgs messageEventArgs)
{
/// Message 객체
var message = messageEventArgs.Message;
/// 예외처리
if (message == null || message.Type != MessageType.TextMessage) return;
/// "/사용자추가" 라는 명령을 받음
if (message.Text.StartsWith("/사용자추가"))
{
dicUserState[message.Chat.Id] = UserState.addUser;
await Bot.SendTextMessageAsync(message.Chat.Id,
@"사용자 이름과 나이를 입력해 주세요.
ex)시나공공,28");
}
/// "/사용자삭제" 라는 명령을 받음
else if (message.Text.StartsWith("/사용자삭제"))
{
dicUserState[message.Chat.Id] = UserState.deleteUser;
await Bot.SendTextMessageAsync(message.Chat.Id, "사용자 이름을 입력해 주세요.");
}
/// "/사용자목록" 라는 명령을 받음
else if (message.Text.StartsWith("/사용자목록"))
{
dicUserState[message.Chat.Id] = UserState.none;
string _message = string.Empty;
Users.ForEach(x => _message += string.Format("이름 : {0}, 나이 : {1}\r\n", x.Name, x.Age) );
await Bot.SendTextMessageAsync(message.Chat.Id, _message);
}
/// "/도움말" 라는 명령을 받음
else if(message.Text.StartsWith("/도움말"))
{
var usage = @"
/사용자추가 - 사용자 추가
/사용자삭제 - 사용자 삭제
/사용자목록 - 사용자 목록
/도움말 - 도움말
";
await Bot.SendTextMessageAsync(message.Chat.Id, usage,
replyMarkup: new ReplyKeyboardHide());
}
/// 그 외 다른 말을 받을 경우 사용자 상태를 보고 적절하게 대응한다.
else
{
/// 예외처리
if(!dicUserState.ContainsKey(message.Chat.Id))
{
await Bot.SendTextMessageAsync(message.Chat.Id, "먼 말인지 모르겠어요.");
return;
}
/// 사용자 상태가 사용자 추가일 경우
if (dicUserState[message.Chat.Id] == UserState.addUser)
{
/// 이름,나이 로 입력을 받을 것이기 때문에 , 로 tokenizing하자
/// 0번은 이름, 1번은 나이
string[] NameAndAge = message.Text.Split(',');
/// 쪼갰는데 개수가 2보다 작으면 잘못된 값을 받았다 판단
if (NameAndAge.Length < 2)
{
await Bot.SendTextMessageAsync(message.Chat.Id,
@"다시 입력해 주세요.
ex)시나공공, 28");
return;
}
/// 이름 나이 셋팅
string _name = NameAndAge[0];
int _age = 0;
bool result = Int32.TryParse(NameAndAge[1], out _age);
/// 나이값이 정상이면 추가
if(result)
{
// DB작업을 해야한다면 여기서 하면 될것같다.
Users.Add(new User(_name, _age));
await Bot.SendTextMessageAsync(message.Chat.Id, message.Text + " 사용자를 추가 했어요.");
dicUserState[message.Chat.Id] = UserState.none;
}
/// 나이값이 이상하면 예외
else
{
await Bot.SendTextMessageAsync(message.Chat.Id,
@"나이가 이상해요.
다시 입력해 주세요.");
}
}
/// 사용자 상태가 사용자 삭제일 경우
else if(dicUserState[message.Chat.Id] == UserState.deleteUser)
{
/// SingleOfDefault로 사용자를 찾았는데 없으면 예외처리 있으면 삭제
var _user = Users.SingleOrDefault(x => x.Name == message.Text);
if (_user != null)
{
// DB작업을 해야한다면 여기서 하면 될것같다.
Users.Remove(_user);
await Bot.SendTextMessageAsync(message.Chat.Id, message.Text + " 사용자를 삭제 했어요.");
dicUserState[message.Chat.Id] = UserState.none;
}
else
{
await Bot.SendTextMessageAsync(message.Chat.Id,
message.Text + @" 사용자가 없어요.
다시 입력해 주세요.");
}
}
/// 사용자 상태가 추가, 삭제가 아닌 경우 시치미 뚝
else
{
await Bot.SendTextMessageAsync(message.Chat.Id, "먼 말인지 모르겠어요.");
}
}
}
}
/// <summary>
/// 사용자를 상태를 나타내는 enum
/// </summary>
public enum UserState
{
addUser,
deleteUser,
none
}
/// <summary>
/// 사용자 클래스, 이름과 나이
/// </summary>
public class User
{
string name = string.Empty;
public string Name
{
get { return name; }
set { name = value; }
}
int age = 0;
public int Age
{
get { return age; }
set { age = value; }
}
public User() : this("none", 0) { }
public User(string name, int age)
{
this.Name = name;
this.Age = age;
}
}
}
이렇게 하면 아래와 같은 결과를 볼 수 있다.
봇에게 말을 걸려면 봇아빠에게 말을 걸었던것과 같이 하면 된다.
나의 경우 @sinagonggongBot을 검색해 대화를 시작했다.
Telegram Bot을 이용해 간단하게 요청을 하고 응답을 받는 서비스를 만들어봤다.
네이버나 다음의 API를 섞어서 사용하거나, 나름의 알고리즘으로 유용한 봇을 만들 수 있을 것 같다.
아 5초마다 이벤트 발생시켜서 사용자한테 메세지 보내는걸 까먹었네. 그건 패스할게요... 귀찮...
키포인트는 Message.Chat.Id를 잘 보관해야 한다.
작년 개발 당시 Message.Chat.Id를 로그인 DB에 컬럼을 추가해 같이 저장해 이용했다.
이슈가 생겼을 때 DB의 TelegramID를 조회해 sendMessage를 했었다.
Group Chat방에 메세지를 보낼땐 https://stackoverflow.com/a/45577773 참조 바랍니다. (Group Chat ID 얻어오기 Tip)
끝.