작년에 클라이언트 프로그램에서 이슈가 발생했을 때 사용자에게 문자를 보내줘야하는 기능을 개발 해야했는데 

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)



끝.

'Programming > 기타' 카테고리의 다른 글

HMAC SHA-256 암호화 (C#, Javascript, Nodejs)  (9) 2018.08.28
noVNC  (597) 2017.01.02

+ Recent posts