# Native Code > برنامه نویسی در Delphi > مباحث عمومی دلفی و پاسکال > حرفه ای: کار با API ربات تلگرام در دلفی 7

## mjdeveloper

با سلام و احترام خدمت دوستان و اساتید بزرگوار

می خوام در این تاپیک طریقه کار با API ربات های تلگرام در دلفی رو خدمت دوستان آموزش بدم . شاید بتونه گره از کار برخی دوستان باز کنه.

طریقه کار به این صورت هست که ابتدا باید در تلگرام ربات خود را بسازید که طریقه ساختش راحت هست و من وارد این مقوله نمیشم.
بعد از ساخت ربات ،تلگرام یه توکن منحصر به فرد برای رباتتون در اختیار شما میذاره که شبیه این هست
123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11
حالا شما باید با آدرس اصلی تلگرام و با استفاده از این توکن با تلگرام در ارتباط باشید. طریقه ارتباط هم Http Post &Get هست. که در کدی که من استفاده کردم برای این کار در دلفی 7 از Indy و کامپوننت TidHttp هست.

درخواست ها رو به ربات تلگرام به صورت زیر ارسال می کنیم.
https://api.telegram.org/bot<token>/METHOD_NAME

پاسخ هایی که ربات تلگرام براتون میفرسته بصورت متنی و در قالب JSON هست.
نمونه یک پاسخ:
{    "ok":true,
    "result":
    [
    {
        "update_id":658807363,
        "message":
        {
            "message_id":41,
            "from":
            {
                "id":79060780,
                "first_name":"mehdi",
                "last_name":"Jafari",
                "username":"ShahvarIMS"
            },
            "chat":
            {
                "id":79060780,
                "first_name":"mehdi",
                "last_name":"Jafari",
                "username":"ShahvarIMS",
                "type":"private"
            },
            "date":1468481903,
            "text":"\/features",
            "entities":
            [{
                "type":"bot_command",
                "offset":0,
                "length":9
            }]
        }
    }
    ]
}



برای کار با این فرمت JSON در دلفی 7 نیاز به کامپوننت و یا کلاس های از پیش طراحی شده دارید و مثل اینکه در دلفی های بالاتر این مشکل رفع شده و دلفی های جدید JSON رو ساپورت میکنن. چون من دیگه سراغشون نرفتم اطلاعات دقیقی ندارم متاسفانه. من در این کد از یک Unit به نام LkJSON v1.07 که در دلفی 7 هم کامپایل میشه استفاده کردم که خدا به این دوست عزیز جناب آقای Leonid Koninin خیرش رو بده انشاالله.

خوب نگران کامپوننت و این unit و کار با JSON نباشید من آخر کد رو میذارم.
دوتا Dll هم کنار برنامه استفاده میشه :
libeay32.dll
ssleay32.dll
دقت کنید اینا ورژن های مختلفی داره که باید با ورژن Indy شما بخونه.

خوب حالا بریم سراغ کد برنامه

ابتدا می بایست یه سری unit ها به بخش use اصلی اضافه کنید که همش رو میذارم
uses  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, IdBaseComponent, IdComponent, IdTCPConnection,
  IdTCPClient, IdHTTP,IdSSLOpenSSL, Buttons;



البته با انداختن کامپوننت TidHttp روی فرم یه سریش اتومات اضافه میشه.

آردس ارسال درخواست به ربات:
  BaseUrl = 'https://api.telegram.org/bot';



گرفتن اطلاعات ربات:
ما می خواین این آدرس رو post کنیم
https://api.telegram.org/bot123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11/getMe

Var  Src : string;
  LHandler: TIdSSLIOHandlerSocket;
begin
  API := edtAPI.Text;
  try
    LHandler := TIdSSLIOHandlerSocket.Create(nil);
    try
      IdHTTP1.ReadTimeout := 30000;
      IdHTTP1.IOHandler:=LHandler;
      LHandler.SSLOptions.Method := sslvTLSv1;
      LHandler.SSLOptions.Mode := sslmUnassigned;
      Src:= IdHTTP1.Get(BaseUrl + API + '/getme');
      memoResponse.lines.clear;
      memoresponse.lines.add(src);
      memoRequest.Text := IdHTTP1.Response.RawHeaders.Text;
    finally
      LHandler.Free;
    end;
  except on E: Exception do
      Showmessage(E.Message);
  end;

خوب اگر یخورده تحقیق کرده باشید بات تلگرام شماره تلفن رو نمیشناسه و فقط با Chat_ID کار میکنه. یعنی به هر شخصی که بخواین پیام بفرستید ابتدا باید Chat_id اون شخص رو در بیارید و سپس به اون پیام مناسب رو ارسال کنید. شخص هنگامی که رباط شما رو ادد میکنه و یه درخواست یا Bot Command رو که با فرمت /CommandName هست رو برای Bot میفرسته با بررسی همین پیام دریافتی میشه Chat_ID فرد رو به دست آورد، تو کد JSON بالا ID حاوی Chat_id فرد هست که نمونه یک پیام ارسالی به Bot هست.
پس با بررسی پیام ارسالی هم میشه Chat_id اون بابا رو بدست آورد و هم اینکه تو Text که متن پیام هست درخواستش رو بررسی کرد و پبام مناسبی براش ارسال کرد.  :لبخند گشاده!: 

با استفاده از این بات می تونید chat_id خودتون رو دربیارید و تو تست برنامه ازش استفاده کنید : 
@get_id_bot

ارسال پیام متنی:
Var  IDUser : String;
  Text : WideString;
  msg : WideString;
  Src : string;
  LHandler: TIdSSLIOHandlerSocket;
begin
  API := edtAPI.Text;
  IDUser := Edit1.Text;
  Text := edtMessage.Text;
  try
    msg := UTF8Encode('chat_id='+IDUser+'&'+'text='+ Text);
    LHandler := TIdSSLIOHandlerSocket.Create(nil);


      IdHTTP1.ReadTimeout := 30000;
      IdHTTP1.IOHandler:=LHandler;
      LHandler.SSLOptions.Method := sslvTLSv1;
      LHandler.SSLOptions.Mode := sslmUnassigned;
      IdHTTP1.HandleRedirects := true;
      memoRequest.Text := BaseUrl + API + '/sendmessage?' + msg;
      Src := IdHTTP1.Get(BotUrl + API +'/sendmessage?' + msg);
      memoResponse.lines.clear;
      memoresponse.lines.add(src);
  except
    on E: EIdHTTPProtocolException do
    begin
      memoRequest.Text := IdHTTP1.Response.RawHeaders.Text;
      if not ((E.ReplyErrorCode = 301) or (E.ReplyErrorCode = 302)) then raise;
      Src := E.ErrorMessage;
      ShowMessage(Src);
    end
  end;

در این کد چون ارتباط ما رو رو تلگرام بصورت اتوماتیک قطع می کنه Indy یک خطا صادر میکنه با متن زیر که الان که تاریخ 26 تیر 95 هست دارم روش کار میکنم ببینم می تونم خفتش کنم یا نه. البته برنامه بدون هیچ مشکلی کار میکنه.
Connection Closed Gracefully

دوستان و اساتید اگه بابت رفع این خطا بتونن راهنمایی کنم ممنون میشم . 

خوب حالا کد زیر پیام هایی رو که از طرف افراد مختلف برای Bot ارسال شده رو در قالب JSON برای شما میاره البته JSON رو من در این کد به اصطلاح Deserialize می کنم.
var  Offset:integer;
  msg : WideString;
  Src : string;
  LHandler: TIdSSLIOHandlerSocket;
begin
   API := edtAPI.Text;
   try
      Offset := 0;
      msg := UTF8Encode('/getUpdates');
      //msg := UTF8Encode('/Update?update_id=1');
      LHandler := TIdSSLIOHandlerSocket.Create(nil);


      IdHTTP1.ReadTimeout := 30000;
      IdHTTP1.IOHandler:=LHandler;
      LHandler.SSLOptions.Method := sslvTLSv1;
      LHandler.SSLOptions.Mode := sslmUnassigned;
      IdHTTP1.HandleRedirects := true;
      memoRequest.Text := BaseUrl + API + msg;
      Src := IdHTTP1.Get(BotUrl + API + msg);
      memUpdates.lines.clear;
      DeserializeJSON(src);
      //memUpdates.lines.add(src);
  except
    on E: EIdHTTPProtocolException do
    begin
      //memoRequest.Text := IdHTTP1.Response.RawHeaders.Text;
      if not ((E.ReplyErrorCode = 301) or (E.ReplyErrorCode = 302)) then raise;
      Src := E.ErrorMessage;
      ShowMessage(Src);
    end
  end;
 اینم کد Deserialize کردن JSON 
procedure TForm1.DeserializeJSON(returnval: string);var
  systems : TStrings;
  returnval2: string;
  json , item , ResultNode , OKNode , msgNode ,fromNode :TlkJSONbase;
  i,j: integer;
  list: TlkJSONObject;
  okVal : boolean;
  nodename : String;
begin
  json:= TlkJSON.ParseText(returnval);
  OKNode := TlkJSONboolean(json).Field['ok'];
  okVal  := OKNode.Value;
  ResultNode := TlkJSONlist(json).Field['result'];
  for i := 0 to pred(ResultNode.Count) do
  begin
    item := TlkJSONlist(ResultNode).child[i];
    msgNode := TlkJSONObject(item).Field['message'];


    memUpdates.Lines.Add('Update_id: ' + VarToStr(item.Field['update_id'].Value));


    if msgNode.Field['from'].Field['id'] <> nil then
      memUpdates.Lines.Add('Chat_id: ' + VarToStr(msgNode.Field['from'].Field['id'].Value));


    if msgNode.Field['from'].Field['first_name'] <> nil then
    memUpdates.Lines.Add('first_name: ' + VarToStr(msgNode.Field['from'].Field['first_name'].Value));


    if msgNode.Field['from'].Field['last_name'] <> nil then
    memUpdates.Lines.Add('last_name: ' + VarToStr(msgNode.Field['from'].Field['last_name'].Value));


    if msgNode.Field['from'].Field['username'] <> nil then
    memUpdates.Lines.Add('username: ' + VarToStr(msgNode.Field['from'].Field['username'].Value));


    if msgNode.Field['text'] <> nil then
    memUpdates.Lines.Add('Message Text: ' + VarToStr(msgNode.Field['text'].Value));


    memUpdates.Lines.Add('-----------------------------');


    end;
end;

متد GetUpdates ربات تلگرام پارامترهای مختلفی داره که می تونید اونا رو از آدرس زیر بررسی بفرمایید که می تونن تعداد خاصی پیام رو خفت کنه و برای شما بیاره. حالا پیام های دریافتی رو می تونید بررسی بفرمایید و اولا Chat_id رو بردارید و تو بانک اطلاعاتی خودتون ذخیره کنید و همچنین پاسخ مناسب به افرادی که کامند خاصی رو درخواست کردن ، بدهید. قاعدتا این بخش خود باید بصور مثال هر چند لحظه هی اجرا بشه تا درخواست ها رو بگیره از BOT. این کار باعث میشه که بات شما کاملا Interactive بشه. متد های تلگرام رو بررسی بفرمایید متد Update هم داره.

درپایان هم عاجزانه تقاضا دارم از دوستانی که این کد رو توسعه می دن . هر کسی به نتیجه های خوبی رسید مثلا ارسال عکس و استیکر و فایل و اینا رو بهش اضافه کردید برای من و بقیه دوستان دیگه هم بذارید. 


کوچیک همه شما.
مهدی جعفری

*توجه: در چند پست پایین تر آخرین نسخه ویرایش شده رو گذاشتم اون رو دانلود بفرمایید*

----------


## JavanSoft

سلام 
ممنون از به اشتراك گذاري اين مطلب

----------


## joker

اینم نسخه XE5 


dllهای سازگار با indy 10
http://indy.fulgan.com/SSL/openssl-1...i386-win32.zip

----------


## mjdeveloper

خیلی سپاسگزارم امیدوارم بقیه دوستان هم تو توسعه این کد کمک کنن تا بقیه هم استفاده کنن.

----------


## mjdeveloper

من روی ارسال تصویر در تلگرام کار میکنم 

کد زیر رو برای post کردن عکس زدم ولی جواب نمیده . خطای read timeout یا unsupported media type میده . اساتید لطفا کمک کنید


varIDUser : String;
Stream: TStringStream;
Params: TIdMultipartFormDataStream;
msg : WideString;
LHandler: TIdSSLIOHandlerSocket;
Src , boundry : string;
begin
  API := edtAPI.Text;
  IDUser := Edit1.Text;
  Stream := TStringStream.Create('');
  try
    Params := TIdMultipartFormDataStream.Create;
    try
      //Params.AddFile('File1', 'C:\test.txt','image/png');
      Params.AddFormField('chat_id',IDUser);
      Params.AddFile('File1', 'E:\image.png','image/png');
      //Data.CopyFrom(Params,0);
      //Params.AddFormField(' test',',');
      try
        msg := '/sendPhoto';
        LHandler := TIdSSLIOHandlerSocket.Create(nil);
        //IdHTTP1.Request.ContentType := 'multipart/form-data';
        IdHTTP1.ReadTimeout := 30000;
        IdHTTP1.IOHandler:=LHandler;
        LHandler.SSLOptions.Method := sslvTLSv1;
        LHandler.SSLOptions.Mode := sslmUnassigned;
        IdHTTP1.HandleRedirects := true;
        IdHTTP1.Request.ContentType := 'multipart/form-data';

        idhttp1.Request.SetHeaders;
        memoResponse.Text :=idhttp1.Request.RawHeaders.Text;
        memoRequest.Text := BaseUrl + API + msg;
        IdHTTP1.Post(BaseUrl + API + msg, Params,Stream);
      except
        on E: Exception do
          showmessage('Error encountered during POST: ' + E.Message+ ': '
          + intToStr(IdHTTP1.Response.ResponseCode))
      end;
      ShowMessage(Stream.DataString);
    finally
      Params.Free;
    end;
  finally
     Stream.Free;
  end;

----------


## mjdeveloper

دوستان سلام 
این هم کد ارسال تصویر با ربات تلگرام

Use IdMultipartFormData

procedure TForm1.SendPostData;var
  Stream: TStringStream;
  Params: TIdMultipartFormDataStream;
  msg : WideString;
  LHandler: TIdSSLIOHandlerSocket;
  IDUser : string;
begin
  API := edtAPI.Text;
  IDUser := Edit1.Text;
  msg := '/sendPhoto';
  Stream := TStringStream.Create('');
  try
   Params := TIdMultipartFormDataStream.Create;
   try
    Params.AddFile('photo', 'E:\image.png','');
    Params.AddFormField('chat_id',IDUser);
    Params.AddFormField('caption','this is a image caption!');
    try
        LHandler := TIdSSLIOHandlerSocket.Create(nil);
        //IdHTTP1.ReadTimeout := 300000;
        IdHTTP1.IOHandler:=LHandler;
        LHandler.SSLOptions.Method := sslvTLSv1;
        LHandler.SSLOptions.Mode := sslmUnassigned;
        IdHTTP1.HandleRedirects := true;
        //idhttp1.Request.ContentType := Params.RequestContentType;
        memoRequest.Text := BaseUrl + API + msg;
        IdHTTP1.Post(BaseUrl + API + msg, Params, Stream);
    except
     on E: Exception do
       ShowMessage('Error encountered during POST: ' + E.Message);
    end;
    ShowMessage(Stream.DataString);
   finally
    Params.Free;
   end;
  finally
   Stream.Free;
  end;
end;

----------


## mjdeveloper

برای ارسال caption فارسی این تیکه کد رو تغییر بدین
Params.AddFormField('caption',UTF8Encode('تست ارسال فارسی'));

----------


## mmx110

درود بر شما جناب جعفری! آیا راجع به حل مشکل پیام خطای صادر شده از Indy راه حلی پیدا کردید؟

----------


## mjdeveloper

دوستان سلام کد ارسال پیام متنی با استفاده از بات تلگرام رو تصحیح کردم که خطای Cannection Closed Carefully رو نده دیگه:

Var
  Stream: TStringStream;
  Params: TIdMultipartFormDataStream;
  IDUser : String;
  Text : WideString;
  msg : WideString;
  Src : string;
  LHandler: TIdSSLIOHandlerSocket;
begin
  API := edtAPI.Text;
  IDUser := Edit1.Text;
  Text := edtMessage.Text;
  try
    try
      msg := '/sendmessage';
      Stream := TStringStream.Create('');
      Params := TIdMultipartFormDataStream.Create;
      Params.AddFormField('chat_id',IDUser);
      Params.AddFormField('text',UTF8Encode('ارسال پيام با استفاده ار بات تلگرام'));


      LHandler := TIdSSLIOHandlerSocket.Create(nil);
      IdHTTP1.ReadTimeout := 30000;
      IdHTTP1.IOHandler:=LHandler;
      LHandler.SSLOptions.Method := sslvTLSv1;
      LHandler.SSLOptions.Mode := sslmUnassigned;
      IdHTTP1.HandleRedirects := true;
      memoRequest.Text := BaseUrl + API + msg;
      IdHTTP1.Post(BaseUrl + API + msg, Params, Stream);
      memoResponse.Text := Stream.DataString;
    finally
      Params.Free;
      Stream.Free;
    ENd;
  except
    on E: EIdHTTPProtocolException do
    begin
      memoRequest.Text := IdHTTP1.Response.RawHeaders.Text;
      if not ((E.ReplyErrorCode = 301) or (E.ReplyErrorCode = 302)) then raise;
      Src := E.ErrorMessage;
      ShowMessage(Src);
    end
  end;
end;

----------


## mjdeveloper

دوستان نسخه جدید و اصلاح شده رو کامل میذارم . ما رو از دعای خیرتون بی نصیب نکنید.

----------


## mmx110

جناب جعفری ضمن عرض تشکر و دعای خیر برای جنابعالی!
ظاهرا یه پیام خطا برای افرادی که  بات ایجاد شده را در تلگرامشان استارت نکرده اند  و یا آن را بلاک کردند صادر می شود...
HTTP/1.1 403 Forbiden
امکان فارسی سازی یا هندل آن هست؟

----------


## mjdeveloper

نمیشه گفت خطا میده . معمولا ایندی برای گزارش روی IDE در حالت Debug این خطا رو نمایش میده. برای خفت کردنش قسمت Exception رو تغییر بدید حله.

procedure TForm1.btnSendMsgClick(Sender: TObject);Var
  Stream: TStringStream;
  Params: TIdMultipartFormDataStream;
  IDUser : String;
  Text : WideString;
  msg : WideString;
  Src : string;
  LHandler: TIdSSLIOHandlerSocket;
begin
  API := edtAPI.Text;
  IDUser := Edit1.Text;
  Text := edtMessage.Text;
  try
    try
      msg := '/sendmessage';
      Stream := TStringStream.Create('');
      Params := TIdMultipartFormDataStream.Create;
      Params.AddFormField('chat_id',IDUser);
      Params.AddFormField('text',UTF8Encode(edtMessage.T  ext));


      LHandler := TIdSSLIOHandlerSocket.Create(nil);
      IdHTTP1.ReadTimeout := 30000;
      IdHTTP1.IOHandler:=LHandler;
      LHandler.SSLOptions.Method := sslvTLSv1;
      LHandler.SSLOptions.Mode := sslmUnassigned;
      IdHTTP1.HandleRedirects := true;
      memoRequest.Text := BaseUrl + API + msg;
      IdHTTP1.Post(BaseUrl + API + msg, Params, Stream);
      memoResponse.Text := Stream.DataString;
    finally
      Params.Free;
      Stream.Free;
    ENd;
  except
    on E: EIdHTTPProtocolException do
    begin
      memoRequest.Text := IdHTTP1.Response.RawHeaders.Text;
      memoResponse.Text :=  IdHTTP1.Response.ResponseText;
      if E.ReplyErrorCode = 403 then
      begin
        ShowMessage('Bot was blocked by the user');
      end;
    end;
  end;
end;

----------


## mohammad_kerman65

با عرض سلام خدمت اساتید محترم.
یه سوال داشتم... میخوام یکسری شماره تلفن(فرض کنید یک فایل اکسل یا ورد شامل 1000 شماره تلفن) بصورت خودکار به تلگرام مخصوص PC  اضافه (ادد) کنم.آیا راهی وجود داره؟   
با تشکر

----------


## mjdeveloper

با سلام خدمت دوستان


ادامه مقاله رو به درخواست دوستان می خوام تکمیل کنم و مباحث حرفه ای در مورد ربات تلگرام و هندلینگ اون با دلفی رو توضیح بدم. و سعی بر اینه که دلفی کارهای محترم از سایر زبان ها تو این زمینه عقب نیفتن.

در این مقاله سعی میکنم نحوه نوشتن سرویس بصورت Thread و مبحث long Polling که قطعا قسمت مهم کار هست رو توضیح بدم و همچنین طریقه افزودن ReplyKeyboardMarkup و InlineKeyboardMarkup و Callbackquery رو و مهم اینکه چطوری از پایگاه داده دیتا مورد نظر رو با توجه به درخواست به طرف نمایش بدیم.

دکمه های ReplyKeyboardMarkup دکمه هایی هستند که زیر بخش نوشتن پیام اضافه میشن و می تونن به عنوان منو استفاده بشن.
دکمه های InlineKeyboardMarkup دکمه هایی هستن که زیر پیام ظاهر میشن و با CallBack Query مدیریت میشن و میشه کلیک کاربر رو فهمید. بطور مثال نظرسنجی

خوب، ما به دو طریق می تونیم با ربات تلگرام ارتباط داشته باشیم و پیام ها و دستوراتی که با بات ارسال میشه رو دریافت کنیم . یکی Webhook هست که قطعا به یک هاست و سرویس اینترنتی نیاز داره، در واقع webhook آدرس اینترنتی هست که به بات معرفی میشه و بات از این به بعد هر پیام رو که دریافت می کنه بصورت اتوماتیک به آدرس Webhook میفرسته.خوب ما اینجا چون یه سرویس ویندوزی می نویسیم با این امکان کار نداریم پس باید خودمون دست به کار بشیم و پیام های بات رو در یک سرویس که تعطیل نمیشه هی واکشی کنیم و جواب مناسب رو به هر پیام بدیم.
قبلا عرض کرده بودم که بات فقط با Chat_id کار میکنه و هر شخص یا بهتر بگیم اکانت تلگرام یه Chat_ID منحصر بفرد داره که می تونیم برای اطلاع رسانی های بعدی این Chat_id رو تو بانک ذخیره کنیم.

خوب بریم سراغ کد نویسی
چیزی که برای شروع کار بهش نیاز داریم یک ربات خشک و خالی هست که با BotFather@ می سازید و Token ربات رو دریافت میکنید. که با چند دستور ساده تلگرام برای شما میسازه.
یک عدد دلفی و اینترنت

در این کد نویسی من از indy و کامپوننت idHttp استفاده می کنم روی دلفی 7 . (کوزه گریم دیگه) 
کنار نرم افزارمون چند تا dll هم استفاده میشه که libeay32.dll و ssleay32.dll هست دقت کنید این Dllها با ورژن ایندی باید مطابقت داشته باشه. ایندی من 9.00.10 هست.

بسیار عالی، کنار نرم افزار ما یه setting.ini هست با Notepad بازش کنید و توکن بات رو توش قرار بدین. کانکشن بانک اطلاعاتی هم می تونه اینجا قرار بگیره . تو این کد من فعلا غیر فعالش کردم . خودتون بخش های غیر فعال شده رو می تونید درست کنید.
(ببخشید یخورده جو گرفته منو بذارین خودمونی تر بگم)

حالا می خوام کدها رو شرح بدم خدمت شما:
uses  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, IdBaseComponent, IdComponent, IdTCPConnection,
  IdTCPClient, IdHTTP,IdSSLOpenSSL, Buttons, IdMultipartFormData, OleCtrls,
  SHDocVw,IniFiles, DB, ADODB, ExtCtrls;
بخش uses که معرفی حضورتون هست

خوب برای اینکه بتونیم Long Polling پیاده کنیم من از thread برای این کار استفاده کردم
type  TBotThread = class(TThread)
  private
  protected
    procedure Execute; override;
    Procedure WriteToLog(st:string);
    procedure SendMessage(ChatID, Text, parse_mode:string;disable_notification:boolean);
    procedure SendMessageAndKey(ChatID,Text,Keys, parse_mode:string;disable_notification:boolean);

    // Get Data From DB  For Learn!
    Function GetDataFromDB(ChatID:String;mode: Integer) : WideString;

Function CheckNumbers(NCode:String) : Boolean;

public
    FActive: Boolean ;
    FidHttpGet : TIdHTTP;
    FidHttpSend : TIdHTTP;
    Fmemo : TMemo;
    FConnection: TADOConnection;
    FAdoDataset : TADODataSet;
    FBotUser : String;
    FShowBotLink : Boolean;
    constructor Create(Active:Boolean;IdHTTPGet,idHttpSend : TIdHTTP;memo : TMemo);
    destructor Destroy;  override;
  end;

در این نرم افزار کوچک ولی هوشمند، من از سه کامپوننت idHttp استفاده می کنم یکی برای گرفتن اطلاعات ربات، یکی برای دریافت پیام های بات و یکی هم برای ارسال پیام.
در کد بالا دو متد SendMessage و SendMessageAndkey رو پیاده کردم که متد اول پیام ساده ارسال می کنه و متد دوم علاوه بر پیام ساده یک کیبورد هم اضافه می کنه که همون منوهای پایین بات هست.
یه سری از کامپوننت های روی فرم اصلی رو ترد همینطوری نمیشناسه پس باید براش تعریف کنیم و موقع ساخت بهش بدیم مثل دو تا  idHttp کانکشن بان و همچنین یه Dataset نام کاربری بات رو برای نمایش ته هر پیام.
متدی هم به نام GetDataFromDB براش تعریف کردم تا داده ها رو از بانک اطلاعاتی بخونه و با توجه به درخواست کاربر بهش نمایش بده. شما می تونید متد های مختلفی داشته باشید که هر متد اطلاعات خاصی رو از بانک واکشی کنه.
یه متد WriteToLog هم گذاشتم تا خطاهای Thread لاگ کنه.

خوب بریم سراغ تعریف های اولیه و ساخت THread
var  fmMain: TfmMain;
  API : string;
  ini : TIniFile;
  ConnStr : string;
  TelegService : TBotThread;


implementation


uses uLkJSON, un_SyncLog, ConstUnit, UGutility;




تنظیماتی مثل Token بات و همچنین کانکشن بانک رو از فایل Setting.ini کنار برنامه می خونم. 
procedure TfmMain.LoadSetting;var
  i, Timecount : integer;
begin
  try
     ini := TIniFile.Create(ExtractFilePath(Application.ExeNam  e)+'Setting.ini');
     Connstr  := ini.ReadString('Server','ConnectionString','');
     API := ini.ReadString('Telegram'   , 'Token' , '');
  finally
     ini.Free;
  end;
end;

این متد پایین لاگ رو پر میکنه:
procedure TfmMain.WriteToLog(st: string);var logFile : TextFile;
  logdate : String;
begin
  AssignFile(logFile, 'TelegLog.txt');
  if FileExists('TelegLog.txt') then
    Append(logFile)
  else
    Rewrite(logFile);
  logdate := DateTimeToStr(Now);
  Writeln(logFile,logdate + ' - ' + st);
  CloseFile(logFile);
end;


procedure TfmMain.ResetLog;
var logFile : TextFile;
begin
  AssignFile(logFile, 'TelegLog.txt');
  Rewrite(logFile);
  CloseFile(logFile);
end;

متد زیر هم اطلاعات بات رو که با متد Getme از تلگرام در قالب json گرفته میشه رو Deserialize می کنه و تو دو تا Label نمایش میده.
procedure TfmMain.DeserializeJSONBotInfo(returnval: string);var
  systems : TStrings;
  returnval2: string;
  json , item , ResultNode , OKNode , msgNode ,fromNode :TlkJSONbase;
  i,j: integer;
  list: TlkJSONObject;
  okVal : boolean;
  nodename : String;
begin
  json:= TlkJSON.ParseText(returnval);
  OKNode := TlkJSONboolean(json).Field['ok'];
  okVal  := OKNode.Value;
  ResultNode := TlkJSONlist(json).Field['result'];
  if okVal then
  begin
      lblBotName.Caption :=  VarToStr(ResultNode.Field['first_name'].Value);
      lblUserName.Caption := '@' + VarToStr(ResultNode.Field['username'].Value);
  end;
end;

خوب حالا بریم سراغ متد های Thread
متد constructor برای ترد که دوتا idHttp و یک Memo رو توش میفرستم تا پیام های دریافتی رو تو Memo مونیتور کنه.
constructor TBotThread.Create(Active: Boolean;IdHTTPGet,idHttpSend : TIdHTTP;memo : TMemo);begin
  inherited Create(True);
  FActive := Active;
  FIDHTTPGet := IdHTTPGet;
  FidHttpSend := idHttpSend;
  Fmemo := memo;
end;

اینم Destroy
destructor TBotThread.Destroy;begin
  inherited Destroy;
end;

خوب متد های SendMessage و SendMessageAndKey رو قبل از متد Execute که متد اصلی ما هست توضیح میدم: ابتدا کل متد و سپس خط به خط توضیحش رو میدم تا براتون روشنتر بشه قضیه:
procedure TBotThread.SendMessage(ChatID, Text, parse_mode: string;disable_notification:boolean);Var
  Stream: TStringStream;
  Params: TIdMultipartFormDataStream;
  //Text : WideString;
  msg : WideString;
  Src : string;
  LHandler: TIdSSLIOHandlerSocket;
begin
  try
    try
      if FShowBotLink then
        Text := Text + LineBreak + FBotUser;
      msg := '/sendmessage';
      Stream := TStringStream.Create('');
      Params := TIdMultipartFormDataStream.Create;
      Params.AddFormField('chat_id',ChatID);
      if parse_mode <> '' then
        Params.AddFormField('parse_mode',parse_mode);
      if disable_notification then
        Params.AddFormField('disable_notification','true')
      else
        Params.AddFormField('disable_notification','false'  );
      Params.AddFormField('disable_web_page_preview','tr  ue');
      Params.AddFormField('text',UTF8Encode(Text));
      LHandler := TIdSSLIOHandlerSocket.Create(nil);
      FidHttpSend.ReadTimeout := 30000;
      FidHttpSend.IOHandler:=LHandler;
      LHandler.SSLOptions.Method := sslvTLSv1;
      LHandler.SSLOptions.Mode := sslmUnassigned;
      FidHttpSend.HandleRedirects := true;
      FidHttpSend.Post(BaseUrl + API + msg, Params, Stream);
    finally
      Params.Free;
      Stream.Free;
    ENd;
  except
    on E: EIdHTTPProtocolException do
    begin
      if E.ReplyErrorCode = 403 then
      begin
        WriteToLog('Bot was blocked by the user');
      end;
    end;
  end;  
end;

متد SendMessage پارامترهایی داره که دوتاش مشخصه پارامتر Parse_mode تو ارسال پیام تلگرام می تونه با مقادیر html و markdown استفاده بشه که نمایانگر فرمت پیام شماست. حالا پیام شما می تنه در قالب html و یا با کاراکتر هایی که برای تلگرام کلاینت معنا و مفهوم خاصی داره، ارسال بشه. برای اینکه بهتر درک کنید یه سر به اینجا بزنید
پارامتر disable_notification هم به پیام میگه بصورت Silent بره یا نه با سر و صدا بره!
یه پارامتر هم به نام disable_web_page_preview تو این کد استفاده کردم که باعث میشه اگر متن پیامتون حاوی لینک باشه پایین پیام Preview این صفحه اینترنتی رو نمایش بده یا نه.
دیتا رو هم بصورت MultipartFormData به سرور تلگرام ارسال میکنیم.

خوب حالا متد SendMessageAndKey رو توضیحش رو خدمت شما بدم.

procedure TBotThread.SendMessageAndKey(ChatID, Text,Keys, parse_mode: string;disable_notification:Boolean);Var
  Stream: TStringStream;
  Params: TIdMultipartFormDataStream;
  msg : WideString;
  Src : string;
  LHandler: TIdSSLIOHandlerSocket;
  Json : TMemoryStream;
  btn : string;
  UTF8 : UTF8String;
  BytesCount : integer;
begin
  btn := keys;
  Json := TMemoryStream.Create;
  UTF8 := AnsiToUtf8(btn);
  BytesCount := Length(UTF8);
  Json.WriteBuffer(UTF8[1], BytesCount);
  Json.Position := 0;
  try
    try
      if FShowBotLink then
        Text := Text + LineBreak + FBotUser;
      msg := '/sendmessage';
      Stream := TStringStream.Create('');
      Params := TIdMultipartFormDataStream.Create;
      Params.AddObject('reply_markup','application/json',Json);
      Params.AddFormField('chat_id',ChatID);
      if parse_mode <> '' then
        Params.AddFormField('parse_mode',parse_mode);
      if disable_notification then
        Params.AddFormField('disable_notification','true')
      else
        Params.AddFormField('disable_notification','false'  );
      Params.AddFormField('text',UTF8Encode(Text));
      LHandler := TIdSSLIOHandlerSocket.Create(nil);
      FidHttpSend.ReadTimeout := 30000;
      FidHttpSend.IOHandler:=LHandler;
      LHandler.SSLOptions.Method := sslvTLSv1;
      LHandler.SSLOptions.Mode := sslmUnassigned;
      FidHttpSend.HandleRedirects := true;
      FidHttpSend.Post(BaseUrl + API + msg, Params, Stream);
    finally
      Params.Free;
      Stream.Free;
    ENd;
  except
    on E: EIdHTTPProtocolException do
    begin
      if E.ReplyErrorCode = 403 then
      begin
        WriteToLog('Bot was blocked by the user');
      end;
    end;
  end;
end;

این متد علاوه بر پارامتر های متد SendMessage یه پارامترKeys هم اضافه داره که این پارامتر حاوی اون کیبوردی هست که می خوای پایین بات یا زیر پیامتون نمایش داده بشه.
این کیبورد ها در قالب شی Json همراه با متن پیام ما ارسال میشه یه نمونه از این کیبوردها اینطوری هست:

  MainMenu = '{"keyboard": [["منوي2","منوي1"],["منوي4", "منوي3"],["ارتباط با ما" ,"منوي5"]], "resize_keyboard": true }';

دو نوع کیبود داریم که یکی ReplyKeyboardMarkup هست و دیگری InlineKeyboardMarkup 

نمونه inline keyboard :
LikeMenuInline = '{"inline_keyboard": [[{ "text": "Delphi", "callback_data": "Delphi" }],[{ "text": "Asp.net", "callback_data": "Asp.net" }],[{ "text": "Java","callback_data": "Java" }]]}';

ReplyKeyboardMarkup در پایین بات ظاهر میشه و InlineKeyboardMarkup پایین پیام ارسالی.

ReplyKeyboardMarkup یک آرایه ای از آرایه های متنی هست که بسته به تعداد آرایه های داخلی، تعداد سطر و ستون دکمه ها تعیین میشه. کاربر با کلیک بر روی هر دکمه باعث میشه متن دکمه ارسال بشه بصورت پیام ساده که حاوی شی JSON Message هست
InlineKeyboardMarkup هم یه آرایه ای از InlineKeyboardButton هست که خود اون فیلدهایی مثل Text و Callback_data یا همون مقداری که برگشت داده میشه هست. کاربر با کلیک روی این دکمه باعث میشه فرمتی متفاوت از پیام معمولی برای بات ارسال بشه که حاوی شی JSON به نام callback_query هست و اطلاعاتی از قیبل ارسال کننده، پیام و مهم تر از همه data هست که همون مقدار callback_data دکمه هست.

نمونه یک دریافت کلیک InlineKeyboardMarkup به صورت json:

'{"ok":true,"result":[{    "update_id":485550139,'#$A'
    "callback_query":{
        "id":"339563465400824028",
        "from":{
            "id":11111111,
            "first_name":"mehdi",
            "last_name":"Jafari",
            "username":"ShahvarIMS"
            },
        "message":{
            "message_id":165,
            "from":{
                "id":200483732,
                "first_name":"Shahvar2Bot",
                "username":"Shahvar2bot"
            },
            "chat":{
                "id":11111111,
                "first_name":"mehdi",
                "last_name":"Jafari",
                "username":"ShahvarIMS",
                "type":"private"
                },
            "date":1474169323,
            "text":"\u0634\u0645\u0627 \u062f\u0631\u062e\u0648\u0627\u0633\u062a \u0641\u0639\u0627\u0644\u0633\u0627\u0632\u064a
            \u0648 \u0627\u0637\u0644\u0627\u0639 \u0631\u0633\u0627\u0646\u064a \u0628\u0631\u0627\u064a
            \u0647\u0646\u0631\u0622\u0645\u0648\u0632 \u0634\u064a\u0631\u0632\u0627\u062f \u0622\u0698\u064a\u0631
            \u0631\u0627 \u062f\u0627\u0631\u064a\u062f \u0622\u064a\u0627 \u062a\u0627\u064a\u064a\u062f
            \u0645\u064a \u0646\u0645\u0627\u064a\u064a\u062f\u061f\n@Shahv  ar2bot",
            "entities":[{
                "type":"mention",
                "offset":90,
                "length":12
                }]
        },
        "data":"1"
    }
}]}'

خوب حالا میرم سراغ قسمت اصلی ماجرا یعنی متد Execute ترد:

procedure TBotThread.Execute;var
  Offset:integer;
  msg : WideString;
  Src : WideString;
  LHandler: TIdSSLIOHandlerSocket;
  last_update_id : int64;
  json , item , ResultNode , OKNode , msgNode ,fromNode :TlkJSONbase;
  i,j: integer;
  list: TlkJSONObject;
  okVal : boolean;
  nodename : String;
  MessageText : WideString;
  ChatID, username , NCode , CallbackData: String;
  IsOKCommand : Boolean;
  SaveRet : integer;
begin
  msg := UTF8Encode('/getUpdates');
  LHandler := TIdSSLIOHandlerSocket.Create(nil);
  FidHttpGet.ReadTimeout := 60000;
  FidHttpGet.IOHandler:=LHandler;
  LHandler.SSLOptions.Method := sslvTLSv1;
  LHandler.SSLOptions.Mode := sslmUnassigned;
  FidHttpGet.HandleRedirects := true;
  last_update_id := 0;
  while not Terminated do
  Begin
       try
          Src := FidHttpGet.Get(BotUrl + API + msg + '?offset=' + IntToStr(last_update_id + 1));
          json:= TlkJSON.ParseText(Src);
          OKNode := TlkJSONboolean(json).Field['ok'];
          okVal  := OKNode.Value;
          ResultNode := TlkJSONlist(json).Field['result'];
          if  ResultNode.Count > 0 then
          Begin
            Fmemo.Clear;
            item := TlkJSONlist(ResultNode).child[ResultNode.Count - 1];
            last_update_id := StrToInt64(VarToStr(item.Field['update_id'].Value));
            for i := 0 to pred(ResultNode.Count) do
            begin
              CallbackData := '';
              IsOKCommand := False;
              MessageText := 'No Message';
              item := TlkJSONlist(ResultNode).child[i];
              if TlkJSONObject(item).Field['message'] <> nil then
              Begin
                  msgNode := TlkJSONObject(item).Field['message'];
                  if msgNode.Field['update_id'] <> nil then
                  Fmemo.Lines.Add('Update_id: ' + VarToStr(item.Field['update_id'].Value));
                  if msgNode.Field['from'].Field['id'] <> nil then
                  Begin
                    ChatID := VarToStr(msgNode.Field['from'].Field['id'].Value);
                    Fmemo.Lines.Add('Chat_id: ' + ChatID);
                  End;
                  if msgNode.Field['from'].Field['first_name'] <> nil then
                  Fmemo.Lines.Add('first_name: ' + VarToStr(msgNode.Field['from'].Field['first_name'].Value));
                  if msgNode.Field['from'].Field['last_name'] <> nil then
                  Fmemo.Lines.Add('last_name: ' + VarToStr(msgNode.Field['from'].Field['last_name'].Value));
                  if msgNode.Field['from'].Field['username'] <> nil then
                  Begin
                    username := VarToStr(msgNode.Field['from'].Field['username'].Value);
                    Fmemo.Lines.Add('username: ' + username);
                  End;
                  if msgNode.Field['text'] <> nil then
                  Begin
                    MessageText := VarToStr(msgNode.Field['text'].Value);
                    Fmemo.Lines.Add('Message Text: ' + MessageText);
                  End;
              End
              Else
              Begin
                  if TlkJSONObject(item).Field['callback_query'] <> nil then
                  Begin
                    msgNode := TlkJSONObject(item).Field['callback_query'];
                    if msgNode.Field['from'].Field['id'] <> nil then
                    Begin
                      ChatID := VarToStr(msgNode.Field['from'].Field['id'].Value);
                      Fmemo.Lines.Add('Chat_id: ' + ChatID);
                    End;
                    if msgNode.Field['from'].Field['first_name'] <> nil then
                      Fmemo.Lines.Add('first_name: ' + VarToStr(msgNode.Field['from'].Field['first_name'].Value));
                    if msgNode.Field['from'].Field['last_name'] <> nil then
                      Fmemo.Lines.Add('last_name: ' + VarToStr(msgNode.Field['from'].Field['last_name'].Value));
                    if msgNode.Field['from'].Field['username'] <> nil then
                    Begin
                      username := VarToStr(msgNode.Field['from'].Field['username'].Value);
                      Fmemo.Lines.Add('username: ' + username);
                    End;
                    if msgNode.Field['data'] <> nil then
                    Begin
                      CallbackData := VarToStr(msgNode.Field['data'].Value);
                      Fmemo.Lines.Add('Callback_data: ' + CallbackData);
                    End;
                  End;
              End;
              if (UpperCase(MessageText) = '/START') OR (UpperCase(MessageText) = '/ACTIVATE') then
              Begin
                SendMessageAndKey(ChatID, 'درود بر شما ' + LineBreak + 'به ربات هوشمند من خوش آمديد'+
                LineBreak + 'تبريک ربات کار ميکنه' ,MainMenu,'',false);


                WriteToLog('ChatID:' + ChatID + ' username:' + username );
                Continue;
                //Sleep(500);
                //SendMessageAndKey(ChatID, 'درود بر شما ' + #13#10 + 'به ربات هوشمند شاهوار خوش آمديد');
              End
              else
              Begin
                if (MessageText = 'No Message') And (CallbackData = '') then
                Begin
                  SendMessage(ChatID, IncorrectChoose,Markdown,True);
                  IsOKCommand := true;
                  Continue;
                End
                Else
                Begin
if CallbackData = 'yes' then
                  Begin
                     SendMessageAndKey(ChatID, congratulation,MainMenu,Markdown,True)
                  End;
                  if CallbackData = 'no' then
                  Begin
                     SendMessageAndKey(ChatID, 'شما خير را انتخاب کرديد!!!',MainMenu,Markdown,True)
                  End;

                  if MessageText = Widestring(Menu_Item1) then
                     SendMessageAndKey(ChatID, PleaseChooseItem,SubMenu,Markdown,True);

                  if MessageText = Widestring(Menu_Item2) then
                     SendMessageAndKey(ChatID, 'آيا مطمئن هستيد؟' + LineBreak + 'نمونه پرسيدن يک سوال از کاربر',ConfirmMenuInLine,Markdown,True);

                  if MessageText = Widestring(Menu_Item3) then
                     SendMessageAndKey(ChatID, 'نمونه نظر سنجي' + LineBreak + 'کدام زبان برنامه نويسي رو دوست داريد؟',LikeMenuInline,Markdown,True);

                  if MessageText = Widestring(Menu_MainMenu) then
                     SendMessageAndKey(ChatID, PleaseChooseItem,MainMenu,Markdown,True);

                  if MessageText = Widestring(Menu_ContactUs) then
                     SendMessage(ChatID, GetCompanyInfo ,Markdown,False);

                  // Get SbMenu Items And Answer it From Data Base
                  if MessageText = Widestring(SubMenu_LastItem) then
                     SendMessageAndKey(ChatID, GetDataFromDB(ChatID,1),SubMenu,HTML,False);
                     
                  if MessageText = Widestring(SubMenu_3Item) then
                     SendMessageAndKey(ChatID, GetDataFromDB(ChatID,2),SubMenu,HTML,False);

                  if MessageText = Widestring(SubMenu_AllItems) then
                     SendMessageAndKey(ChatID, GetDataFromDB(ChatID,3),SubMenu,HTML,False);

                  if CallbackData = 'Delphi' then
                  Begin
                     SendMessageAndKey(ChatID, 'شما ' + CallbackData + ' را انتخاب کرديد',MainMenu,Markdown,True)
                  End;

                  if CallbackData = 'Asp.net' then
                  Begin
                     SendMessageAndKey(ChatID, 'شما ' + CallbackData + ' را انتخاب کرديد',MainMenu,Markdown,True)
                  End;

                  if CallbackData = 'Java' then
                  Begin
                     SendMessageAndKey(ChatID, 'شما ' + CallbackData + ' را انتخاب کرديد',MainMenu,Markdown,True)
                  End;
                End;
                
                //SendMessage(ChatID, 'شما گزينه ' + '"' +  MessageText + '" ' + 'را انتخاب کرديد');
                  Sleep(500);
              End;

            end;
          End
          Else
              Continue;
      except
        on E: EIdHTTPProtocolException do
        begin
          if not ((E.ReplyErrorCode = 301) or (E.ReplyErrorCode = 302)) then raise;
          Src := E.ErrorMessage;
          ShowMessage(Src);
        end
      end;
   ENd;
end;

بسیار خوب این متد رو توضیحش رو شروع میکنم:
در این متد با استفاده از متد GetUpdates تلگرام ما پیام های ارسالی به بات رو واکشی میکنیم. این متد یه پارامتر به نام Offset داره که می تونه حاوی Update_id یک پیام باشه. چنانچه GetUpdate بدون پارامتر فراخونی بشه تلگرام 100 پیام آخر بات رو برای ما میاره . ولی ما نمی خوای هی پیامهایی که پاسخ داده شده رو دوباره بیاره پس offset رو با آخرین update_id+1 پر میکنیم اینکار باعث میشه پیامهای پیشین بصورت اتوماتیک تلگرام اونها رو confirm کنه و دیگه تو getupdate نیاد.

مبحث Long polling رو که خدمت شما عرض کردم این حلقه اون رو پوشش میده یعنی تا زمانی که بات پیام داره باید پاسخگو باشیم .
while not Terminated do
  Begin

واکشی پیام ها: این خط از کد پیام ها رو واکشی میکنه: ما ابتدا last_update_id برابر صفر میذاریم و بعد با آخرین update_id پرش میکنیم.

Src := FidHttpGet.Get(BotUrl + API + msg + '?offset=' + IntToStr(last_update_id + 1));

پیامهای واکشی شده در Src بصورت json پر مشن و با تیکه کد زیر اونها رو deserialize میکنیم که ResultNode حاوی پیام ها خواهد بود.
json:= TlkJSON.ParseText(Src);          OKNode := TlkJSONboolean(json).Field['ok'];
          okVal  := OKNode.Value;
          ResultNode := TlkJSONlist(json).Field['result'];

حالا در حلقه For پایین تمام پیام ها رو بررسی میکنیم و پاسخ مناسب هر پیام رو میدیم.
if  ResultNode.Count > 0 then          Begin
            Fmemo.Clear;
            item := TlkJSONlist(ResultNode).child[ResultNode.Count - 1];
            last_update_id := StrToInt64(VarToStr(item.Field['update_id'].Value));
            for i := 0 to pred(ResultNode.Count) do
            begin

این تیکه کد حاوی یک پیام از مجموعه پیام های بات هست
item := TlkJSONlist(ResultNode).child[i];

حالا ممکنه دو حالت پیش بیاد یک اینکه پیام حاوی شی Message باشه یا حاوی callback_query . اگر تو کد دقت کنید بنده دو حالت رو با if چک کردم . 

if TlkJSONObject(item).Field['message'] <> nil then
و
if TlkJSONObject(item).Field['callback_query'] <> nil then
تو حالت اول یه پیام ساده یا کلیک روی ReplaykeyboardMarkup هست  و تو حالت دوم InlineKeyboard کلیک شده.

خوب حالا ما متن پیام یا Callback_data رو تو IFهای بالا دریافت می کنیم و همچنین مشخصات پیام و نفرارسالی و مهمتراز همه Chat_id فرد رو.
حالا پاسخگویی رو شروع میکنیم

if (UpperCase(MessageText) = '/START') OR (UpperCase(MessageText) = '/ACTIVATE') then              Begin
                SendMessageAndKey(ChatID, 'درود بر شما ' + LineBreak + 'به ربات هوشمند من خوش آمديد'+
                LineBreak + 'تبريک ربات کار ميکنه' ,MainMenu,'',false);

WriteToLog('ChatID:' + ChatID + ' username:' + username );
                Continue;
                //Sleep(500);
                //SendMessageAndKey(ChatID, 'درود بر شما ' + #13#10 + 'به ربات هوشمند شاهوار خوش آمديد');
              End
              else
              Begin
                if (MessageText = 'No Message') And (CallbackData = '') then
                Begin
                  SendMessage(ChatID, IncorrectChoose,Markdown,True);
                  IsOKCommand := true;
                  Continue;
                End
                Else
                Begin
                  
                  if CallbackData = 'yes' then
                  Begin
                     SendMessageAndKey(ChatID, congratulation,MainMenu,Markdown,True)
                  End;
                  if CallbackData = 'no' then
                  Begin
                     SendMessageAndKey(ChatID, 'شما خير را انتخاب کرديد!!!',MainMenu,Markdown,True)
                  End;

                  if MessageText = Widestring(Menu_Item1) then
                     SendMessageAndKey(ChatID, PleaseChooseItem,SubMenu,Markdown,True);

                  if MessageText = Widestring(Menu_Item2) then
                     SendMessageAndKey(ChatID, 'آيا مطمئن هستيد؟' + LineBreak + 'نمونه پرسيدن يک سوال از کاربر',ConfirmMenuInLine,Markdown,True);

                  if MessageText = Widestring(Menu_Item3) then
                     SendMessageAndKey(ChatID, 'نمونه نظر سنجي' + LineBreak + 'کدام زبان برنامه نويسي رو دوست داريد؟',LikeMenuInline,Markdown,True);

                  if MessageText = Widestring(Menu_MainMenu) then
                     SendMessageAndKey(ChatID, PleaseChooseItem,MainMenu,Markdown,True);

if MessageText = Widestring(Menu_ContactUs) then
                     SendMessage(ChatID, GetCompanyInfo ,Markdown,False);

                  // Get SbMenu Items And Answer it From Data Base
                  if MessageText = Widestring(SubMenu_LastItem) then
                     SendMessageAndKey(ChatID, GetDataFromDB(ChatID,1),SubMenu,HTML,False);
                     
                  if MessageText = Widestring(SubMenu_3Item) then
                     SendMessageAndKey(ChatID, GetDataFromDB(ChatID,2),SubMenu,HTML,False);


                  if MessageText = Widestring(SubMenu_AllItems) then
                     SendMessageAndKey(ChatID, GetDataFromDB(ChatID,3),SubMenu,HTML,False);

                  if CallbackData = 'Delphi' then
                  Begin
                     SendMessageAndKey(ChatID, 'شما ' + CallbackData + ' را انتخاب کرديد',MainMenu,Markdown,True)
                  End;

                  if CallbackData = 'Asp.net' then
                  Begin
                     SendMessageAndKey(ChatID, 'شما ' + CallbackData + ' را انتخاب کرديد',MainMenu,Markdown,True)
                  End;

                  if CallbackData = 'Java' then
                  Begin
                     SendMessageAndKey(ChatID, 'شما ' + CallbackData + ' را انتخاب کرديد',MainMenu,Markdown,True)
                  End;

                End;

خوب کسی که اولین بار بات شما رو ادد میکنه ابتدا با کامند Start/ شروع میکنه و باقی ماجرا دیگه تیکه کد بالا مشخصه که متن پیام با گزینه هایی مقایسه می شه و پاسخ داده میشه و با هر پاسخ امکان داره Keyboard نمایش داده شده تو بات تغییر کنه.

این کد دکمه استارت سرویس:
procedure TfmMain.btnStartServiceClick(Sender: TObject);begin
   TelegService := TBotThread.Create(True,IdHTTP1,IdHTTP3,memoRespons  e);
   TelegService.FConnection := schlConn;
   TelegService.FAdoDataset := ADODataSet1;
   TelegService.FBotUser := lblUserName.Caption;
   TelegService.FShowBotLink := True;
   TelegService.FreeOnTerminate := true;
   TelegService.Resume;
   btnStartService.Enabled := false;
   btnStopService.Enabled := true;
end;

*توجه: حتما ابتدا یه بات بسازید و توکن اون رو تو setting.ini بذارید و سپس اجرا کنید*

خوب در پایان هم کد براتون میذارم ولی حدود چهار ماه چشام در اومد تا به اینجا رسید  :لبخند گشاده!: 
یا علی مدد
دوستدار همتون هستم  :بوس: 
مهدی جعفری

----------


## h_mohamadi

با سلام خدمت جناب آقای جعفری عزیز
واقعا عالی و کامل بود و بسیار ممنون از شما که اینقدر کامل و جامع زحمت کشید

----------


## mjdeveloper

دوستان پیام های خصوصی مبنی بر خطای بستن سرویس تلگرام و گرفتن  Access Violation گزارش شده که کد های زیر رو تغییر بدید در کد:
دکمه Start:
TelegService.FreeOnTerminate := False;
—------------------—
procedure TfmMain.btnStopServiceClick(Sender: TObject);
begin
  if Assigned(TelegService) then
  begin
    TelegService.Free;
  end;
   btnStartService.Enabled := true;
   btnStopService.Enabled := False;
end;


procedure TfmMain.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  if Assigned(TelegService) then
  begin
    TelegService.Free;
  end;
end;




ممنون بابت گزارش خطا : انشاالله با اینکار رفع میشه.

----------


## aboualfazl

> دوستان پیام های خصوصی مبنی بر خطای بستن سرویس تلگرام و گرفتن  Access Violation گزارش شده که کد های زیر رو تغییر بدید در کد:
> دکمه Start:
> TelegService.FreeOnTerminate := False;
> —------------------—
> procedure TfmMain.btnStopServiceClick(Sender: TObject);
> begin
>   if Assigned(TelegService) then
>   begin
>     TelegService.Free;
> ...


اولا ممنون از زحمات شما .

این کد رو اصلاح کنید :

procedure TfmMain.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  if  btnStopService.Enabled = True   then
    begin
 if Assigned(TelegService) then
  begin
    TelegService.Free;
  end;
  end;
end;

----------


## mojtaba_bahrami

سلام و تشکر فراوان بابت اطلاعات ارزشمندی که قرار دادین.
من یه سوال دارم. ربات من با تغییراتی که روش انجام دادم کامل کار می کنه ولی وقتی از متد SetChatAction استفاده می کنم و دو پارامتر Chat_id , action رو ست می کنم، خطای 400 میده.
اگه ممکنه راهنمایی بفرمایید.

  msg := '/sendChatAction';
  try
    Stream := TStringStream.Create('');
    Params := TIdMultipartFormDataStream.Create;
    Params.AddFormField('chat_id',ChatID);
    Params.AddFormField('action',ChatAction);
    try
      LHandler := TIdSSLIOHandlerSocket.Create(nil);
//      FidHttpSend.ReadTimeout := 300000;
      FidHttpSend.IOHandler:=LHandler;
      LHandler.SSLOptions.Method := sslvTLSv1;
      LHandler.SSLOptions.Mode := sslmUnassigned;
      FidHttpSend.HandleRedirects := true;
      //idhttp1.Request.ContentType := Params.RequestContentType;
      FidHttpSend.Post(BaseUrl + API + msg, Params, Stream);

    except
      on E: Exception do
      begin
        ShowMessage(Stream.DataString);
        ShowMessage('Error encountered during POST: ' + E.Message);
      end;  
    end;
  finally
    ShowMessage(Stream.DataString);
    Params.Free;
    Stream.Free;
  end;

----------


## h_mohamadi

سلام
ممنون از راهنمایی آموزش خوبیتان
می خواستم بدانم چطوری می شود  از کاربر یک سوال پرسید و بعد برای کاربر یک کی برد اعدادی آورد و  بعد  ببینیم کاربر چه کد ملی را زده و بعد در دیتابیس سرچش کنیم
مثلا کد ملی را وارد کند و بعد از زدن کد ملی ربات جستجو کند و بگوید شما در دیتابیس هستید یا نه؟؟
خیلی تلاش کردم ولی جوابی که کاربر می دهد را نمی توانم ببینم
ممنون می شوم راهنمایی بفرمائید


                  if MessageText = Widestring(Menu_Item2) then
                      SendMessageAndKey(ChatID, 'کد ملي را وارد نمائيد',answerInline,Markdown,True);

----------


## h_mohamadi

از اساتید محترم خواهشمندم اگر در این مورد می توانند کمک بفرمایند

----------


## mjdeveloper

> سلام
> ممنون از راهنمایی آموزش خوبیتان
> می خواستم بدانم چطوری می شود  از کاربر یک سوال پرسید و بعد برای کاربر یک کی برد اعدادی آورد و  بعد  ببینیم کاربر چه کد ملی را زده و بعد در دیتابیس سرچش کنیم
> مثلا کد ملی را وارد کند و بعد از زدن کد ملی ربات جستجو کند و بگوید شما در دیتابیس هستید یا نه؟؟
> خیلی تلاش کردم ولی جوابی که کاربر می دهد را نمی توانم ببینم
> ممنون می شوم راهنمایی بفرمائید
> 
> 
>                   if MessageText = Widestring(Menu_Item2) then
>                       SendMessageAndKey(ChatID, 'کد ملي را وارد نمائيد',answerInline,Markdown,True);


برای کار با بانک اطلاعاتی و محاوره با کاربر و همچنین ذخیره مقادیری که کاربر ارسال میکنه شما می تونید یه جدول برای ذخیره Chat_ID با فیلدهای دیگه ای که نیاز دارید از کاربر بگیرید و ذخیره کنید داشته باشید و مهم تر از همه یه فیلد به نام RequestStatus بصورت رشته یا عددی داشته در این جدول باشید  تا مرحله به مرحله وضعیت کاربر رو داشته باشید و رصد کنید. مثال می زنم:
1.کاربر ربات رو استارت می کنه شما حداقل chat_id رو تو جدول ثبت می کنید
2 . به کاربر پیغام میدهید "لطفا کد ملی را وارد کنید" . و بلافاصله بعد از دادن این پیام RequestStatus را برابر با مثلا مقدار EnterNCode برای این کاربر قرار می دهید
3. از این به بعد پیام کاربر رو بعنوان کد ملی در نظر می گیرید
ReqStatus = GetRequestStatus(Chat_ID);// get from db
if ReqStatus='EnterNCode' then
Begin
NCode := MessageText;
//search DB and etc.
End;
4.  بطور مثال طرف تو بانک اطلاعاتی شما با کد ملی یافت میشه و بعد بهش میگید رمزش رو وارد کنه و بلافاصله بعد از این پیام شما RequestStatus را برابر با EnterPassword قرار میدید و پیام بعدی کاربر رو رمز در نظر میگیرید.
ReqStatus = GetRequestStatus(Chat_ID);
if ReqStatus='EnterPassword' then
Begin
password := MessageText;
//search DB and etc.
End;
در ابتدای متد Execute باید RequestStatus یا همان مرحله ای کاربر قرار داره رو بگیرید و در ادامه این مراحل رو چک کنید و پیام های کاربر را با توجه به وضعیت فعلی، مقادیر فیلدهای درخواستی خود قلمداد کنید
به نظر بنده بهترین راه یا میشه گفت تنها راه همینه.
اینم پیشنهاد: برای گرفتن یه مقدار مثل کد ملی از کاربر بهتره خودش تایپ کنه گرفتن این مقدار با کیبورد ساختگی شما خیلی داستان داره و باید عدد بع عدد گرفته بشه ذخیره بشه و status نگهدارید
امیدوارم کمکتون کنه.

----------


## mojtaba_bahrami

سلام
سپاس از پست فوق العاده کاربردی تون
من این ربات رو راه اندازی کردم و همه چیز خوب کار می کرد، ولی امروز که برنامه رو اجرا می کنم خطای Error Connecting With SSL میده. با توجه به اینکه تا دیروز همه چیز خوب بود، به نظرتون چی باعث این اتفاق شده؟
من از indy 9.0.10 استفاده می کردم، ارتقاء دادم به 9.0.18 و بعد ارتقاء به 10 متناسب با هرکدوم هم DLL های متناسب رو کپی کردم ولی مشکل برطرف نشد.
اگه دوستان راه حلی دارن ممنون میشم ارائه بدن.
از طرفی DLL هایی که توی این پست ارائه شده بود رو وقتی کپی می کنم پیغام خطای Could Not Load SSL Library میده. ولی وقتی DLL های ورژن 9 رو استفاده می کنم پیام Error Connecting With SSL

----------


## h_mohamadi

سلام دوستان
می خواستم ببینم چطوری می شود  که ربات بتواند یک تصویر را هم ارسال کند

----------


## alipil

> سلام
> سپاس از پست فوق العاده کاربردی تون
> من این ربات رو راه اندازی کردم و همه چیز خوب کار می کرد، ولی امروز که برنامه رو اجرا می کنم خطای Error Connecting With SSL میده. با توجه به اینکه تا دیروز همه چیز خوب بود، به نظرتون چی باعث این اتفاق شده؟
> من از indy 9.0.10 استفاده می کردم، ارتقاء دادم به 9.0.18 و بعد ارتقاء به 10 متناسب با هرکدوم هم DLL های متناسب رو کپی کردم ولی مشکل برطرف نشد.
> اگه دوستان راه حلی دارن ممنون میشم ارائه بدن.
> از طرفی DLL هایی که توی این پست ارائه شده بود رو وقتی کپی می کنم پیغام خطای Could Not Load SSL Library میده. ولی وقتی DLL های ورژن 9 رو استفاده می کنم پیام Error Connecting With SSL



سلام خدمت دوستان
این خطا Error Connecting With SSL برای من هم اتفاق می افتد. و فقط  مشکل من این خطاست. این خطا هنگامی رخ می دهد که برنامه میخواهد کد
Src := IdHTTP1.Get(BotUrl + API + msg); 
را اجرا کند.
لطفا برای رفع این خطا راهنمایی فرمایید.

----------


## h_mohamadi

> سلام خدمت دوستان
> این خطا Error Connecting With SSL برای من هم اتفاق می افتد. و فقط  مشکل من این خطاست. این خطا هنگامی رخ می دهد که برنامه میخواهد کد
> Src := IdHTTP1.Get(BotUrl + API + msg); 
> را اجرا کند.
> لطفا برای رفع این خطا راهنمایی فرمایید.


من هم این مشکل را دارم؟
مورد از چی است؟

----------


## alipil

> من هم این مشکل را دارم؟
> مورد از چی است؟



واقعا نمیدونم مشکل از کجاست، اما اینو میدونم که فقط اولین بار که برنامه اجرا میشه و میخواد اطلاعات رو ارسال کنه یا دریافت کنه این ارور داده میشه. بعدش دیگه برنامه خیلی عالی کارشو انجام میده!
فکر کنم اگر بتونیم این ارور رو مخفی کنیم، مشکل حل بشه. البته من نتونستم این ارور رو مخفی کنم.
کلا برنامه ارور زیاد داره که باید کنترل بشه. مثلا وقتی اینترنت برای دقایقی قطع میشه، پشت سر هم ارور میده. این ها باید نمایش داده نشه. چون وقتی اینترنت وصل میشه, برنامه به کارش ادامه میده!

از دوستان اگر کسی هست که میتونه ارورهای برنامه رو مخفی کنه، بگه که ما هم انجام بدیم.
متشکرم

----------


## alipil

سلام دوست من
اول اینکه در فایل ضمیمه ای که ارسال کردید، یه فایلی کم هست که باعث شده برنامه اجرا نشه!
دوم اینکه من مبحث دریافت جواب از منوی  InlineKeyboardMarkup  رو اصلا متوجه نشدم و وقتی میخوام از کدهای شما توی برنامه ام استفاده کنم، اصلا برنامه کار نمیکنه و گزینه کلیک شده رو برنمیگردونه!
سومین مطلب اینکه اگر بخواهیم به همراه متنمون اموجی هم ارسال کنیم، یا متنهای حامل اموجی رو دریافت کنیم، همچنین برای دریافت یک فایل (فایل، تصویر، صوت، فیلم) چه کدی رو استفاده کنیم؟
ممنون میشم جواب بدید. :لبخند:

----------


## mjdeveloper

مشکل _Error Connecting With SSL با ارتقا به indy 10 باید حل بشه و من این کار رو کردم و مشکل حل شد و دیگه این خطا رو دریافت نکردم._

----------


## mjdeveloper

> سلام دوست من
> اول اینکه در فایل ضمیمه ای که ارسال کردید، یه فایلی کم هست که باعث شده برنامه اجرا نشه!
> دوم اینکه من مبحث دریافت جواب از منوی  InlineKeyboardMarkup  رو اصلا متوجه نشدم و وقتی میخوام از کدهای شما توی برنامه ام استفاده کنم، اصلا برنامه کار نمیکنه و گزینه کلیک شده رو برنمیگردونه!
> سومین مطلب اینکه اگر بخواهیم به همراه متنمون اموجی هم ارسال کنیم، یا متنهای حامل اموجی رو دریافت کنیم، همچنین برای دریافت یک فایل (فایل، تصویر، صوت، فیلم) چه کدی رو استفاده کنیم؟
> ممنون میشم جواب بدید.


1. دوست عزیز من از همین کد استفاده می کنم و خیلی از دوستان دیگه هم بر داشتن و مثل اینکه درست بوده و تونستن اجرا کنن.
2. برای دریافت فایل هم هنوز کد نزدم که آماده داشته باشم ولی روند کار بصورت زیر هست . که اگر شما زحمتش رو کشیدید برای بقیه هم بذارید و همچنین برای من.

دریافت فایل:
1.کاربر عکس به ربات ارسال می کنه .
2. ربات شما با getupdate پیام رو دریافت میکنه و نتیجه شبیه زیر هست
{"ok":true,"result":
    [{
        "update_id":839925807,
        "message":{
        "message_id":61,
        "from":
        {
            "id":79060780,
            "first_name":"mehdi",
            "last_name":"Jafari",
            "username":"MjDeveloper",
            "language_code":"en-US"
        },
        "chat":
        {
            "id":79060780,
            "first_name":"mehdi",
            "last_name":"Jafari",
            "username":"MjDeveloper",
            "type":"private"
        },
        "date":1499596170,
        "photo":[{
            "file_id":"AgADBAAD_6gxG1EpEFPNIr8YHIITG08YqRkABMY  H8HZUe1inQm0DAAEC",
            "file_size":2734,
            "width":90,
            "height":90}
            ,
            {"file_id":"AgADBAAD_6gxG1EpEFPNIr8YHIITG08YqRkABF  U7V07oQYu0QW0DAAEC",
            "file_size":14179,
            "width":200,
            "height":200
            }
        ]}
    }]
}



3.شما File_ID رو بر می دارید و مجدد پست می کنید به تلگرام و پاسخی حاوی File_path دریافت می کنید
4. مثال ارسال پست file_id . به آدرس دقت کنید
https://api.telegram.org/bot<token>/getfile?file_id=AgADBAAD_6gxG1EpEFPNIr8YHIITG08YqR  kABFU7V07oQYu0QW0DAAEC
5. نتیجه پست کردن file_id به نلگرام شبیه زیر هست که file_path رو داره
{"ok":true,"result":{"file_id":"AgADBAAD_6gxG1EpEF  PNIr8YHIITG08YqRkABFU7V07oQYu0QW0DAAEC","file_size  ":14179,"file_path":"photos/file_0.jpg"}}
6. حالا با استفاده از file_path می تونید لینک دانلود رو بسازید و دانلود کنید
https://api.telegram.org/file/bot<token>/<file_path>
برای دریافت عکس:
https://api.telegram.org/file/bot<token>/photos/file_0.jpg
دانلود شروع میشه

---------------- برای فایل های دیگه مانند document-----------------
فقط مسیر دانلود متفاوت میشه

{"ok":true,"result":{"file_id":"BQADBAAD8AADUSkQU1  4d-TTauBAyAg","file_size":100140,"file_path":"documen  ts/esfControl.png"}}

https://api.telegram.org/file/bot<token>/documents/esfControl.png

امیدوارم کمکتون کنه.
حالا اگر فرصت کنم کد براش میزنم و میذارم ولی اگر کسی از دوستان زحمتش رو کشید ممنون میشم کد بذاره بقیه هم استفاده کنن.

کد دریافت فایل را در این پست مشاهده بفرمایید

----------


## alipil

> 1. دوست عزیز من از همین کد استفاده می کنم و خیلی از دوستان دیگه هم بر داشتن و مثل اینکه درست بوده و تونستن اجرا کنن.
> 2. برای ارسال فایل هم هنوز کد نزدم که آماده داشته باشم ولی روند کار بصورت زیر هست . که اگر شما زحمتش رو کشیدید برای بقیه هم بذارید و همچنین برای من.
> 
> دریافت فایل:
> 1.کاربر عکس به ربات ارسال می کنه .
> 2. ربات شما با getupdate پیام رو دریافت میکنه و نتیجه شبیه زیر هست
> {"ok":true,"result":
>     [{
>         "update_id":839925807,
> ...




===========


ممنونم ممنونم
خیلی خوشحال شدم که جوابمو دادین.
حتما اگر به دست آوردی رسیدم به دوستان ارائه میدم. 

من dll‌ های مربوط به نسخه 10 رو جایگزین کردم ، indy 10 ‌رو نتونستم دانلود و نصب کنم. اگر indy 10 رو در دلفی نصب کنم، مشکل برطرف میشه؟

مشکل ارورها موقع قطع اینترنت چطور برطرف میشه؟

بازم ممنونم از پاسخگویی

----------


## alipil

من با استفاده از مطالب شما، این ربات @GUinfobot رو ساختم و یک کیس هم به عنوان سرور همیشه روشن گذاشتم تا روبات همیشه قابل استفاده باشه.
به نظرم ضعف استفاده از دلفی همین باشه که باید یک کیس یا سرور همیشه روشن و آنلاین باشه تا روبات بتونه کار کنه. ولی نقطه قوتش اینه که که نیاز نیست هزینه های سالانه خرید هاست و ssl‌ بدیم. و محدودیت حجم برای دیتای روبات هم نداریم.

----------


## alipil

> با سلام خدمت دوستان
> 
> ....
> 
> *توجه: حتما ابتدا یه بات بسازید و توکن اون رو تو setting.ini بذارید و سپس اجرا کنید*
> 
> خوب در پایان هم کد براتون میذارم ولی حدود چهار ماه چشام در اومد تا به اینجا رسید 
> یا علی مدد
> دوستدار همتون هستم 
> مهدی جعفری




این خط ارور میده 
UGutility in 'UGutility.pas';

ظاهرا این فایل باید باشه که نیست...
[Fatal Error] D7_Teleg.dpr(9): File not found: 'RzEdit.dcu'

----------


## mjdeveloper

> این خط ارور میده 
> UGutility in 'UGutility.pas';
> 
> ظاهرا این فایل باید باشه که نیست...
> [Fatal Error] D7_Teleg.dpr(9): File not found: 'RzEdit.dcu'



یه سری unit های اضافی که برای پروژه خودم هست رو می تونید حذف کنید از هر یونیتی خطا داشتید حذف کنید

----------


## mjdeveloper

> من با استفاده از مطالب شما، این ربات @GUinfobot رو ساختم و یک کیس هم به عنوان سرور همیشه روشن گذاشتم تا روبات همیشه قابل استفاده باشه.
> به نظرم ضعف استفاده از دلفی همین باشه که باید یک کیس یا سرور همیشه روشن و آنلاین باشه تا روبات بتونه کار کنه. ولی نقطه قوتش اینه که که نیاز نیست هزینه های سالانه خرید هاست و ssl‌ بدیم. و محدودیت حجم برای دیتای روبات هم نداریم.


آفرین بر شما دوست عزیز. 
موفق باشید

----------


## mjdeveloper

> ===========
> 
> 
> ممنونم ممنونم
> خیلی خوشحال شدم که جوابمو دادین.
> حتما اگر به دست آوردی رسیدم به دوستان ارائه میدم. 
> 
> من dll‌ های مربوط به نسخه 10 رو جایگزین کردم ، indy 10 ‌رو نتونستم دانلود و نصب کنم. اگر indy 10 رو در دلفی نصب کنم، مشکل برطرف میشه؟
> 
> ...


پروژه باید مجدد روی indy 10 کامپایل بشه و تغییراتی در کد لازم هست : و همچنین dll های ایندی 10 رو کنار برنامه بذارید. صرفا با گذاشتن dll ها کنار نسخه قدیمی مشکل حل نمیشه.

توابع ارسال پیام متنی و ارسال پیام متنی حاوی کیبورد اینطوری خواهد شد:

procedure TBotThread.SendMessage(ChatID:String; Text : WideString; parse_mode:string;disable_notification:boolean);Va  r
  Stream: TStringStream;
  Params: TIdMultipartFormDataStream;
  //Text : WideString;
  msg : WideString;
  Src : string;
  LHandler: TIdSSLIOHandlerSocketOpenSSL;
begin
  try
    try
      if FShowBotLink then
        Text := Text + LineBreak + FBotUser;
      msg := '/sendmessage';
      Stream := TStringStream.Create('');
      Params := TIdMultipartFormDataStream.Create;
      Params.AddFormField('chat_id',ChatID);
      if parse_mode <> '' then
        Params.AddFormField('parse_mode',parse_mode);
      if disable_notification then
        Params.AddFormField('disable_notification','true')
      else
        Params.AddFormField('disable_notification','false'  );
      Params.AddFormField('disable_web_page_preview','tr  ue');
      Params.AddFormField('text',UTF8Encode(Text),'utf-8').ContentTransfer:='8bit';
      //Params.AddFormField('text',Text);
      LHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
      FidHttpSend.ReadTimeout := 30000;
      FidHttpSend.IOHandler:=LHandler;
      LHandler.SSLOptions.Method := sslvTLSv1;
      LHandler.SSLOptions.Mode := sslmClient;
      FidHttpSend.HandleRedirects := true;
      //FidHttpSend.Request.CharSet:= 'UTF-8';
      FidHttpSend.Post(BaseUrl + API + msg, Params, Stream);
    finally
      LHandler.Free;
      Params.Free;
      Stream.Free;
    ENd;
  except
    on E: EIdHTTPProtocolException do
    begin
      if E.ErrorCode = 403 then
      begin
        WriteToLog('Bot was blocked by the user');
      end;
    end;
  end;  
end;


procedure TBotThread.SendMessageAndKey(ChatID:String; Text : WideString;Keys, parse_mode:string;disable_notification:boolean);
Var
  Stream: TStringStream;
  Params: TIdMultipartFormDataStream;
  msg : WideString;
  Src : string;
  LHandler: TIdSSLIOHandlerSocketOpenSSL;
  Json : TMemoryStream;
  btn : string;
  UTF8 : UTF8String;
  BytesCount : integer;
  SaveMsgID : boolean;
begin
  btn := keys;
  SaveMsgID := False;
  if pos('force_reply',btn) > 0 then
     SaveMsgID := True;
  Json := TMemoryStream.Create;
  UTF8 := AnsiToUtf8(btn);
  BytesCount := Length(UTF8);
  Json.WriteBuffer(UTF8[1], BytesCount);
  Json.Position := 0;
  try
    try
      if FShowBotLink then
        Text := Text + LineBreak + FBotUser;
      msg := '/sendmessage';
      Stream := TStringStream.Create('');
      Params := TIdMultipartFormDataStream.Create;
      Params.AddObject('reply_markup','application/json','UTF-8',Json);
      Params.AddFormField('chat_id',ChatID);
      if parse_mode <> '' then
        Params.AddFormField('parse_mode',parse_mode);
      if disable_notification then
        Params.AddFormField('disable_notification','true')
      else
        Params.AddFormField('disable_notification','false'  );
      Params.AddFormField('text',UTF8Encode(Text),'utf-8').ContentTransfer:='8bit';
      LHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
      FidHttpSend.ReadTimeout := 30000;
      FidHttpSend.IOHandler:=LHandler;
      LHandler.SSLOptions.Method := sslvTLSv1;
      LHandler.SSLOptions.Mode := sslmClient;
      FidHttpSend.HandleRedirects := true;
      FidHttpSend.Post(BaseUrl + API + msg, Params, Stream);
      //fmMain.Memo1.Text := Stream.DataString;
      if SaveMsgID then
        SaveMessageID(Stream.DataString,ChatID);
    finally
      Json.Free;
      LHandler.Free;
      Params.Free;
      Stream.Free;
    ENd;
  except
    on E: EIdHTTPProtocolException do
    begin
      if E.ErrorCode = 403 then
      begin
        WriteToLog('Bot was blocked by the user');
      end;
    end;
  end;
end;

----------


## mjdeveloper

دوستان خوشحال میشم ربات های نوشته شده با این کد رو معرفی بفرمایید و یا تو زمینه ارسال به تلگرام تو چه نرم افزار هایی از این کد استفاده کردید

----------


## alipil

> دوستان سلام 
> این هم کد ارسال تصویر با ربات تلگرام
> 
> Use IdMultipartFormData
> 
> procedure TForm1.SendPostData;var
>   Stream: TStringStream;
>   Params: TIdMultipartFormDataStream;
>   msg : WideString;
> ...



سلام
همونطور که آقای مهدی جعفری عزیز فرموده بودن، من دیشب روی کد ارسال فایل کار کردم و قرار شد اگر به نتیجه ای رسیدم، اینجا بگم تا همه دوستان استفاده کنن.
ارسال فایلها به دو صورت است:
1. ارسال هر فایلی صرفا به صورت فایل مثل : فایل متنی، عکس، فیلم، صوت، apk، exe ، dll , ....... 
2. ارسال فایلهایی با فرمت متداول و قابل نمایش در خود تلگرام (عکس، فیلم، صوت)


1. اگر میخواهید هر فایل رو ارسال کنید، فقط کافیه در کدهای بالا به جای /sendPhoto از /senddocument استفاده کنید و نیز پارامتر photo را به document تغییر دهید.
2. اگر میخواهید فایلهایی با فرمت متداول قابل نمایش در تلگرام رو ارسال کنید، باید نوع فایل رو مشخص نمایید. مثلا در کدهای فوق که آقای جعفری نوشتن، چون قرار بوده عکس ارسال کنند، از عبارت Photo , sendphoto استفاده شده است. پس برای ارسال ویدئو باید از عبارت video  و sendvideo  استفاده کرد. در این صورت است که عکس در خود برنامه تلگرام قابل نمایش است و خود تلگرام قادر است که ویدئو را پلی کند! حالا اگر همین عکس یا فیلم به صورت document ارسال شود، با آن مانند یک فایل رفتار شده و گوشی از شما میخواهد تا یک برنامه را جهت نمایش آن انتخاب کنید. مثلا عکس توسط گالری نمایش داده میشه و ویدئو با یک برنامه ای که برای نمایش فیلم نصب کردید نمایش داده میشه و ...

----------


## alipil

> پروژه باید مجدد روی indy 10 کامپایل بشه و تغییراتی در کد لازم هست : و همچنین dll های ایندی 10 رو کنار برنامه بذارید. صرفا با گذاشتن dll ها کنار نسخه قدیمی مشکل حل نمیشه.
> 
> توابع ارسال پیام متنی و ارسال پیام متنی حاوی کیبورد اینطوری خواهد شد:
> 
> procedure TBotThread.SendMessage(ChatID:String; Text : WideString; parse_mode:string;disable_notification:boolean);Va  r
>   Stream: TStringStream;
>   Params: TIdMultipartFormDataStream;
>   //Text : WideString;
>   msg : WideString;
> ...



خیلی متشکرم آقای جعفری، در مورد ارسال پیام متنی با منویی که زیر آن هست یعنی (InlineKeyboardMarkup) مشکلی نیست، فقط وقتی که کاربر یکی از گزینه های منو رو انتخاب میکنه، اصلا نمیتونم گزینه ی انتخاب شده رو دریافت کنم. 
برنامه ارور میده. در مرحله ی دریافت اطلاعات از روبات (سرور تلگرام) ارور میده. به طوری که حتی اگر بعد از انتخاب منو، پیامهای دیگه ای از سوی کاربر به ربات ارسال شده باشه، اونها رو هم نمیتونه دریافت کنه و کلا برنامه از کار می افته.
اگر ممکنه خیلی شسته و رفته تر بفرمایید که چجوری متوجه بشم کدوم گزینه انتخاب شده.
متشکرم

----------


## mjdeveloper

استفاده از Emoji:

برخی ایموجی ها رو من کد html رو در آوردم چون خیلی زیاده همش وقت نشد:
یونیت رو به پروژه ادد کنید


طریقه استفاده:
SendMessage(ChatID,'متن شما'+ DoubleExclamationMark_Emoji,HTML,False);

----------


## mjdeveloper

دوستان چون من کم وقت می کنم به اینجا سر بزنم اگر سوالی کاری داشتید می تونید تو تلگرام در خدمت شما باشم

@mjdeveolper

----------


## mjdeveloper

با درود و احترام خدمت دوستان گل

دریافت فایل توسط ربات تلگرام رو به درخواست دوستان براتون آماده کردم . این کد برای دریافت تصویر و فایل در قالب های photo و document هست فرصت نکردم فرمت های دیگه رو بررسی کنم ببینم نام قالبش چیه . دیگه این و بعهده خودتون میذارم

Recieve And Download File From bot with delphi

const
  BotFileUrl = 'https://api.telegram.org/file/bot';

مرحله بعد افزودن دو متد به botThread هست:

  TBotThread = class(TThread)
  private
    Function  GetFile_Path(file_id: string) : String;
    procedure DownloadFile(FilePath , OrginalFileName: String );


بدنه این دو متد بشرح زیر هست:
function TBotThread.GetFile_Path(file_id: string): String;Var
  Stream: TStringStream;
  Params: TIdMultipartFormDataStream;
  msg : WideString;
  Src : string;
  LHandler: TIdSSLIOHandlerSocketOpenSSL;
  json , ResultNode , OKNode:TlkJSONbase;
begin
  Result := '';
  try
    try
      msg := '/getfile';
      LHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
      FidHttpSend.ReadTimeout := 30000;
      FidHttpSend.IOHandler:=LHandler;
      LHandler.SSLOptions.Method := sslvTLSv1;
      LHandler.SSLOptions.Mode := sslmClient;
      FidHttpSend.HandleRedirects := true;
      Src := FidHttpGet.Get(BotUrl + API + msg + '?file_id=' + file_id);
      json:= TlkJSON.ParseText(Src);
      OKNode := TlkJSONboolean(json).Field['ok'];
      ResultNode := TlkJSONlist(json).Field['result'];
      if  ResultNode <> nil then
      Begin
          Result := VarToStr(ResultNode.Field['file_path'].Value);
      End;
    finally
      LHandler.Free;
    ENd;
  except
    on E: EIdHTTPProtocolException do
    begin
      if E.ErrorCode = 403 then
      begin
        WriteToLog('Bot was blocked by the user');
      end;
    end;
  end;  
end;


procedure TBotThread.DownloadFile(FilePath , OrginalFileName: String );
var
  Stream: TMemoryStream;
  Url, Filename: String;
  LHandler: TIdSSLIOHandlerSocketOpenSSL;
  tmpFilePath : String;
Begin
  if  OrginalFileName = '' then
  Begin
      tmpFilePath := AnsiReplaceStr(FilePath,'/','\');
      OrginalFileName := ExtractFileName(tmpFilePath);
  End;
  LHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
  FidHttpSend.ReadTimeout := 0;
  FidHttpSend.IOHandler := LHandler;
  LHandler.SSLOptions.Method := sslvTLSv1;
  LHandler.SSLOptions.Mode := sslmClient;
  FidHttpSend.HandleRedirects := true;
  URL := BotFileUrl+ API + '/' + FilePath;
  Stream := TMemoryStream.Create;
  try
    FidHttpSend.Get(URL, Stream);
    Stream.SaveToFile(OrginalFileName);
  finally
    Stream.Free;
  end;
end;

حالا باید متد اصلی ترد یعنی Execute رو یه تغییری بدیم:


procedure TBotThread.Execute;
var
....
  file_id , file_path , FileName: string;


فسمت دریافت پیام رو هم بخش های زیر رو بهش اضافه کنید

if TlkJSONObject(item).Field['message'] <> nil then
Begin
    msgNode := TlkJSONObject(item).Field['message'];
.
.
.
.
    //Download file


    if (msgNode.Field['document'] <> nil) then
    Begin
        file_id := VarToStr(msgNode.Field['document'].Field['file_id'].Value);
        FileName := VarToStr(msgNode.Field['document'].Field['file_name'].Value);
        file_path := GetFile_Path(file_id);
        if file_path <> '' then
          DownloadFile(file_path,FileName);
    End;


    //Download file
    if (msgNode.Field['photo'] <> nil) then
    Begin
        file_id := VarToStr(msgNode.Field['photo'].Child[msgNode.Field['photo'].Count-1].Field['file_id'].Value);
        FileName := '';
        file_path := GetFile_Path(file_id);
        if file_path <> '' then
          DownloadFile(file_path,FileName);
    End;
End

 تصاویر و فایل ها کنار فایل برنامه شما دانلود میشه
توجه داشته باشید کد با indy 10 نوشته شده شاید روی 9 هم کار کنه البته . تست نکردم .

یا علی
ایام بکام

----------


## SayeyeZohor

> من روی ارسال تصویر در تلگرام کار میکنم 
> 
> کد زیر رو برای post کردن عکس زدم ولی جواب نمیده . خطای read timeout یا unsupported media type میده . اساتید لطفا کمک کنید
> 
> 
> varIDUser : String;
> Stream: TStringStream;
> Params: TIdMultipartFormDataStream;
> msg : WideString;
> ...



کدتون غیر قابل فهمه

----------


## h_mohamadi

خیلی خیلی ممنونم دست شما دردنکنه

----------


## mjdeveloper

> کدتون غیر قابل فهمه


این مشکل برای اوایل کار روی بات بود و حلش کردم.

----------


## Amir Lajevardi

با سلام و تشکر از آموزش بسیار کاربردی شما
فقط در حال حاضر که تلگرام  فیل تر شده دسترسی به API ربات فقط از طریق چیز پی ان امکان پذیره، آیا راهی وجود داره که بشه توسط خود IdHTTP محدودیت رو رفع کرد؟؟  :متفکر:

----------


## Mask

> با سلام و تشکر از آموزش بسیار کاربردی شما
> فقط در حال حاضر که تلگرام  فیل تر شده دسترسی به API ربات فقط از طریق چیز پی ان امکان پذیره، آیا راهی وجود داره که بشه توسط خود IdHTTP محدودیت رو رفع کرد؟؟


میتونین از پروکسی استفاده کنین.

----------


## Amir Lajevardi

> میتونین از پروکسی استفاده کنین.


ممنون میشم راهنمایی کنید از چه پروتکل پروکسی میشه در IdHTTP استفاده کرد؟
MTProto رو پشتیبانی میکنه؟
حالا مهم نیست سرویس رایگان هم باشه هر سرویسی که بشه استفاده کرد اگر معرفی کنید سپاسگزارم...

----------


## Mask

پروکسی خودش یک پروتکله. از همون استفاده کنین.
https

----------


## mjdeveloper

جهت گنجاندن Proxy داخلی برای سرویس می تواند مانند زیر عمل کنید

ProxyEnable: Boolean; 
 ProxyAuth: Boolean;
  ProxyServer: String;
  ProxyPort: String;
  ProxyUsername: String;
  ProxyPassword: String;

procedure TfmMain.FormCreate(Sender: TObject);begin
  lblVersion.Caption := 'Version ' + Version;
  LoadSetting;


  if ProxyEnable Then
  Begin
      if  ProxyPort = '' then ProxyPort := '0';


      IdHTTP1.ProxyParams.BasicAuthentication := ProxyAuth;
      IdHTTP1.ProxyParams.ProxyServer := ProxyServer;
      IdHTTP1.ProxyParams.ProxyPort := StrToInt(ProxyPort);
      IdHTTP1.ProxyParams.ProxyUsername := ProxyUsername;
      IdHTTP1.ProxyParams.ProxyPassword := ProxyPassword;


      IdHTTP2.ProxyParams.BasicAuthentication := ProxyAuth;
      IdHTTP2.ProxyParams.ProxyServer := ProxyServer;
      IdHTTP2.ProxyParams.ProxyPort := StrToInt(ProxyPort);
      IdHTTP2.ProxyParams.ProxyUsername := ProxyUsername;
      IdHTTP2.ProxyParams.ProxyPassword := ProxyPassword;


      IdHTTP3.ProxyParams.BasicAuthentication := ProxyAuth;
      IdHTTP3.ProxyParams.ProxyServer := ProxyServer;
      IdHTTP3.ProxyParams.ProxyPort := StrToInt(ProxyPort);
      IdHTTP3.ProxyParams.ProxyUsername := ProxyUsername;
      IdHTTP3.ProxyParams.ProxyPassword := ProxyPassword;


  End;

----------

