PDA

View Full Version : حرفه ای: کار با API ربات تلگرام در دلفی 7



mjdeveloper
شنبه 26 تیر 1395, 08:38 صبح
با سلام و احترام خدمت دوستان و اساتید بزرگوار

می خوام در این تاپیک طریقه کار با 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
شنبه 26 تیر 1395, 10:32 صبح
سلام
ممنون از به اشتراك گذاري اين مطلب

joker
شنبه 26 تیر 1395, 10:45 صبح
اینم نسخه XE5


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

mjdeveloper
شنبه 26 تیر 1395, 10:50 صبح
خیلی سپاسگزارم امیدوارم بقیه دوستان هم تو توسعه این کد کمک کنن تا بقیه هم استفاده کنن.

mjdeveloper
سه شنبه 29 تیر 1395, 10:13 صبح
من روی ارسال تصویر در تلگرام کار میکنم

کد زیر رو برای 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
پنج شنبه 31 تیر 1395, 16:30 عصر
دوستان سلام
این هم کد ارسال تصویر با ربات تلگرام


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
پنج شنبه 31 تیر 1395, 16:49 عصر
برای ارسال caption فارسی این تیکه کد رو تغییر بدین

Params.AddFormField('caption',UTF8Encode('تست ارسال فارسی'));

mmx110
دوشنبه 04 مرداد 1395, 22:41 عصر
درود بر شما جناب جعفری! آیا راجع به حل مشکل پیام خطای صادر شده از Indy راه حلی پیدا کردید؟

mjdeveloper
سه شنبه 05 مرداد 1395, 12:46 عصر
دوستان سلام کد ارسال پیام متنی با استفاده از بات تلگرام رو تصحیح کردم که خطای 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
سه شنبه 05 مرداد 1395, 13:23 عصر
دوستان نسخه جدید و اصلاح شده رو کامل میذارم . ما رو از دعای خیرتون بی نصیب نکنید.

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

mjdeveloper
یک شنبه 10 مرداد 1395, 09:31 صبح
نمیشه گفت خطا میده . معمولا ایندی برای گزارش روی 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
پنج شنبه 04 شهریور 1395, 14:28 عصر
با عرض سلام خدمت اساتید محترم.
یه سوال داشتم... میخوام یکسری شماره تلفن(فرض کنید یک فایل اکسل یا ورد شامل 1000 شماره تلفن) بصورت خودکار به تلگرام مخصوص PC اضافه (ادد) کنم.آیا راهی وجود داره؟
با تشکر

mjdeveloper
یک شنبه 28 شهریور 1395, 16:44 عصر
با سلام خدمت دوستان


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

در این مقاله سعی میکنم نحوه نوشتن سرویس بصورت 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 و یا با کاراکتر هایی که برای تلگرام کلاینت معنا و مفهوم خاصی داره، ارسال بشه. برای اینکه بهتر درک کنید یه سر به اینجا (https://core.telegram.org/bots/api#markdown-style) بزنید
پارامتر 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 (https://core.telegram.org/bots/api#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
دوشنبه 29 شهریور 1395, 08:05 صبح
با سلام خدمت جناب آقای جعفری عزیز
واقعا عالی و کامل بود و بسیار ممنون از شما که اینقدر کامل و جامع زحمت کشید

mjdeveloper
دوشنبه 29 شهریور 1395, 11:41 صبح
دوستان پیام های خصوصی مبنی بر خطای بستن سرویس تلگرام و گرفتن 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
سه شنبه 02 آذر 1395, 16:26 عصر
دوستان پیام های خصوصی مبنی بر خطای بستن سرویس تلگرام و گرفتن 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;




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

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

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


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
یک شنبه 31 اردیبهشت 1396, 11:06 صبح
سلام و تشکر فراوان بابت اطلاعات ارزشمندی که قرار دادین.
من یه سوال دارم. ربات من با تغییراتی که روش انجام دادم کامل کار می کنه ولی وقتی از متد 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
سه شنبه 23 خرداد 1396, 17:37 عصر
سلام
ممنون از راهنمایی آموزش خوبیتان
می خواستم بدانم چطوری می شود از کاربر یک سوال پرسید و بعد برای کاربر یک کی برد اعدادی آورد و بعد ببینیم کاربر چه کد ملی را زده و بعد در دیتابیس سرچش کنیم
مثلا کد ملی را وارد کند و بعد از زدن کد ملی ربات جستجو کند و بگوید شما در دیتابیس هستید یا نه؟؟
خیلی تلاش کردم ولی جوابی که کاربر می دهد را نمی توانم ببینم
ممنون می شوم راهنمایی بفرمائید


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

h_mohamadi
شنبه 27 خرداد 1396, 13:01 عصر
از اساتید محترم خواهشمندم اگر در این مورد می توانند کمک بفرمایند

mjdeveloper
پنج شنبه 15 تیر 1396, 07:24 صبح
سلام
ممنون از راهنمایی آموزش خوبیتان
می خواستم بدانم چطوری می شود از کاربر یک سوال پرسید و بعد برای کاربر یک کی برد اعدادی آورد و بعد ببینیم کاربر چه کد ملی را زده و بعد در دیتابیس سرچش کنیم
مثلا کد ملی را وارد کند و بعد از زدن کد ملی ربات جستجو کند و بگوید شما در دیتابیس هستید یا نه؟؟
خیلی تلاش کردم ولی جوابی که کاربر می دهد را نمی توانم ببینم
ممنون می شوم راهنمایی بفرمائید


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
سه شنبه 17 مرداد 1396, 09:02 صبح
سلام
سپاس از پست فوق العاده کاربردی تون
من این ربات رو راه اندازی کردم و همه چیز خوب کار می کرد، ولی امروز که برنامه رو اجرا می کنم خطای 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
پنج شنبه 02 شهریور 1396, 08:48 صبح
سلام دوستان
می خواستم ببینم چطوری می شود که ربات بتواند یک تصویر را هم ارسال کند

alipil
دوشنبه 13 شهریور 1396, 23:29 عصر
سلام
سپاس از پست فوق العاده کاربردی تون
من این ربات رو راه اندازی کردم و همه چیز خوب کار می کرد، ولی امروز که برنامه رو اجرا می کنم خطای 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
یک شنبه 26 شهریور 1396, 12:17 عصر
سلام خدمت دوستان
این خطا
Error Connecting With SSL برای من هم اتفاق می افتد. و فقط مشکل من این خطاست. این خطا هنگامی رخ می دهد که برنامه میخواهد کد

Src := IdHTTP1.Get(BotUrl + API + msg);
را اجرا کند.
لطفا برای رفع این خطا راهنمایی فرمایید.

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

alipil
شنبه 01 مهر 1396, 08:45 صبح
من هم این مشکل را دارم؟
مورد از چی است؟


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

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

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

mjdeveloper
شنبه 01 مهر 1396, 11:39 صبح
مشکل
Error Connecting With SSL با ارتقا به indy 10 باید حل بشه و من این کار رو کردم و مشکل حل شد و دیگه این خطا رو دریافت نکردم.

mjdeveloper
شنبه 01 مهر 1396, 12:01 عصر
سلام دوست من
اول اینکه در فایل ضمیمه ای که ارسال کردید، یه فایلی کم هست که باعث شده برنامه اجرا نشه!
دوم اینکه من مبحث دریافت جواب از منوی
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_6gxG1EpEFPNIr8YHIITG08YqRkABMYH8HZUe1inQm 0DAAEC",
"file_size":2734,
"width":90,
"height":90}
,
{"file_id":"AgADBAAD_6gxG1EpEFPNIr8YHIITG08YqRkABFU7V07oQYu0QW 0DAAEC",
"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_6gxG1EpEFPNIr8YHIITG08YqRkABFU7V07oQYu0QW 0DAAEC","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":"BQADBAAD8AADUSkQU14d-TTauBAyAg","file_size":100140,"file_path":"documents/esfControl.png"}}


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

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

کد دریافت فایل را در این پست (http://barnamenevis.org/showthread.php?526969-%DA%A9%D8%A7%D8%B1-%D8%A8%D8%A7-API-%D8%B1%D8%A8%D8%A7%D8%AA-%D8%AA%D9%84%DA%AF%D8%B1%D8%A7%D9%85-%D8%AF%D8%B1-%D8%AF%D9%84%D9%81%DB%8C-7&p=2377987&viewfull=1#post2377987) مشاهده بفرمایید

alipil
شنبه 01 مهر 1396, 12:43 عصر
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_6gxG1EpEFPNIr8YHIITG08YqRkABMYH8HZUe1inQm 0DAAEC",
"file_size":2734,
"width":90,
"height":90}
,
{"file_id":"AgADBAAD_6gxG1EpEFPNIr8YHIITG08YqRkABFU7V07oQYu0QW 0DAAEC",
"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_6gxG1EpEFPNIr8YHIITG08YqRkABFU7V07oQYu0QW 0DAAEC","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":"BQADBAAD8AADUSkQU14d-TTauBAyAg","file_size":100140,"file_path":"documents/esfControl.png"}}


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

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



===========


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

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

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

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

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

alipil
شنبه 01 مهر 1396, 21:21 عصر
با سلام خدمت دوستان

....

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

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



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

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

mjdeveloper
یک شنبه 02 مهر 1396, 09:40 صبح
این خط ارور میده
UGutility in 'UGutility.pas';

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


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

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

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

mjdeveloper
یک شنبه 02 مهر 1396, 09:47 صبح
===========


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

من 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
یک شنبه 02 مهر 1396, 09:53 صبح
دوستان خوشحال میشم ربات های نوشته شده با این کد رو معرفی بفرمایید و یا تو زمینه ارسال به تلگرام تو چه نرم افزار هایی از این کد استفاده کردید

alipil
یک شنبه 02 مهر 1396, 10:15 صبح
دوستان سلام
این هم کد ارسال تصویر با ربات تلگرام


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;


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


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

alipil
یک شنبه 02 مهر 1396, 10:29 صبح
پروژه باید مجدد روی 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;


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

mjdeveloper
یک شنبه 02 مهر 1396, 10:48 صبح
استفاده از Emoji:

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


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

mjdeveloper
دوشنبه 03 مهر 1396, 09:25 صبح
دوستان چون من کم وقت می کنم به اینجا سر بزنم اگر سوالی کاری داشتید می تونید تو تلگرام در خدمت شما باشم

@mjdeveolper (https://telegram.me/mjdeveloper)

mjdeveloper
پنج شنبه 06 مهر 1396, 13:38 عصر
با درود و احترام خدمت دوستان گل

دریافت فایل توسط ربات تلگرام رو به درخواست دوستان براتون آماده کردم . این کد برای دریافت تصویر و فایل در قالب های 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
چهارشنبه 12 مهر 1396, 20:08 عصر
من روی ارسال تصویر در تلگرام کار میکنم

کد زیر رو برای 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;



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

h_mohamadi
یک شنبه 16 مهر 1396, 16:11 عصر
خیلی خیلی ممنونم دست شما دردنکنه

mjdeveloper
چهارشنبه 19 مهر 1396, 15:55 عصر
کدتون غیر قابل فهمه

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

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

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

Amir Lajevardi
شنبه 05 آبان 1397, 17:44 عصر
میتونین از پروکسی استفاده کنین.

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

Mask
دوشنبه 21 آبان 1397, 12:50 عصر
پروکسی خودش یک پروتکله. از همون استفاده کنین.
https

mjdeveloper
یک شنبه 30 تیر 1398, 16:02 عصر
جهت گنجاندن 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;