Search code examples
delphiunicodedelphi-7emojiindy-9

Send emoji with indy delphi7


i want to send emoji with indy 9.00.10 on delphi 7. i use tnt VCL Controls . i found this url http://apps.timwhitlock.info/emoji/tables/unicode for unicode and bytes code. how to convert this codes to delphi Constants for Send with indy.

i use this delphi code for send message to telegram bot:

procedure TBotThread.SendMessage(ChatID:String; Text : WideString;
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','true');
      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;

bytes sample for emojies:

AERIAL_TRAMWAY = '\xf0\x9f\x9a\xa1';
AIRPLANE = '\xe2\x9c\x88';
ALARM_CLOCK = '\xe2\x8f\xb0';
ALIEN_MONSTER = '\xf0\x9f\x91\xbe';

sorry for bad english!!!


Solution

  • The Telegram Bot API supports several forms of input:

    We support GET and POST HTTP methods. We support four ways of passing parameters in Bot API requests:

    • URL query string
    • application/x-www-form-urlencoded
    • application/json (except for uploading files)
    • multipart/form-data (use to upload files)

    You are using the last option.

    Indy 9 does not support Delphi 2009+ or Unicode. All uses of string are assumed to be AnsiString, which is the case in Delphi 7. Any AnsiString you add to TIdMultipartFormDataStream or TStrings, even a UTF-8 encoded one, will be transmitted as-is by TIdHTTP. However, there is no option to specify to the server that the string data is actually using UTF-8 as a charset. But, according to the docs:

    All queries must be made using UTF-8.

    So not specifying an explicit charset might not be problem.

    If you still have problems with multipart/form-data, then consider using application/x-www-form-urlencoded (use TIdHTTP.Post(TStrings)) or application/json (use TIdHTTP.Post(TStream)) instead:

    procedure TBotThread.SendMessage(ChatID: String; Text: WideString; parse_mode: string; disable_notification: boolean);
    var
      Params: TStringList;
      LHandler: TIdSSLIOHandlerSocket;
    begin
      if FShowBotLink then
        Text := Text + LineBreak + FBotUser;
    
      Params := TStringList.Create;
      try
        Params.Add('chat_id=' + UTF8Encode(ChatID));
        if parse_mode <> '' then
          Params.Add('parse_mode=' + UTF8Encode(parse_mode));
        if disable_notification then
          Params.Add('disable_notification=true')
        else
          Params.Add('disable_notification=false');
        Params.Add('disable_web_page_preview=true');
        Params.Add('text=' + UTF8Encode(Text));
    
        LHandler := TIdSSLIOHandlerSocket.Create(nil);
        try
          LHandler.SSLOptions.Method := sslvTLSv1;
          LHandler.SSLOptions.Mode := sslmClient;
    
          FidHttpSend.HandleRedirects := true;
          FidHttpSend.ReadTimeout := 30000;
          FidHttpSend.IOHandler := LHandler;
          try
            try
              FidHttpSend.Post(BaseUrl + API + '/sendmessage', Params, TStream(nil));
            except
              on E: EIdHTTPProtocolException do
              begin
                if E.ReplyErrorCode = 403 then
                begin
                  WriteToLog('Bot was blocked by the user');
                end;
              end;
            end;  
          finally
            FidHttpSend.IOHandler := nil;
          end;
        finally
          LHandler.Free;
        end;
      finally
        Params.Free;
      end;
    end;
    

    procedure TBotThread.SendMessage(ChatID: String; Text: WideString; parse_mode: string; disable_notification: boolean);
    var
      Params: TStringStream;
      LHandler: TIdSSLIOHandlerSocket;
    
      function JsonEncode(const wStr: WideString): string;
      var
        I: Integer;
        Ch: WideChar;
      begin
        // JSON uses UTF-16 text, so no need to encode to UTF-8...
        Result := '';
        for I := 1 to Length(wStr) do
        begin
          Ch := wStr[i];
          case Ch of
            #8: Result := Result + '\b';
            #9: Result := Result + '\t';
            #10: Result := Result + '\n';
            #12: Result := Result + '\f';
            #13: Result := Result + '\r';
            '"': Result := Result + '\"';
            '\': Result := Result + '\\';
            '/': Result := Result + '\/';
          else
            if (Ord(Ch) >= 32) and (Ord(Ch) <= 126) then
              Result := Result + AnsiChar(Ord(wStr[i]))
            else
              Result := Result + '\u' + IntToHex(Ord(wStr[i]), 4);
          end;
        end;
      end;
    
    begin
      if FShowBotLink then
        Text := Text + LineBreak + FBotUser;
    
      Params := TStringStream.Create('');
      try
        Params.WriteString('{');
        Params.WriteString('chat_id: "' + JsonEncode(ChatID) + '",');
        if parse_mode <> '' then
          Params.WriteString('parse_mode: "' + JsonEncode(parse_mode) + '",')
        if disable_notification then
          Params.WriteString('disable_notification: True,')
        else
          Params.WriteString('disable_notification: False,');
        Params.WriteString('disable_web_page_preview: True,');
        Params.WriteString('text: "' + JsonEncode(Text) + '"');
        Params.WriteString('}');
        Params.Position := 0;
    
        LHandler := TIdSSLIOHandlerSocket.Create(nil);
        try
          LHandler.SSLOptions.Method := sslvTLSv1;
          LHandler.SSLOptions.Mode := sslmClient;
    
          FidHttpSend.HandleRedirects := true;
          FidHttpSend.ReadTimeout := 30000;
          FidHttpSend.IOHandler := LHandler;
          try
            try
              FidHttpSend.Request.ContentType := 'application/json';
              FidHttpSend.Post(BaseUrl + API + '/sendmessage', Params, TStream(nil));
            except
              on E: EIdHTTPProtocolException do
              begin
                if E.ReplyErrorCode = 403 then
                begin
                  WriteToLog('Bot was blocked by the user');
                end;
              end;
            end;  
          finally
            FidHttpSend.IOHandler := nil;
          end;
        finally
          LHandler.Free;
        end;
      finally
        Params.Free;
      end;
    end;
    

    That being said, your function's Text parameter is a WideString, which uses UTF-16, so you should be able to send any Unicode text, including emojis. If you are trying to generate text in your code, just make sure you UTF-16 encode any non-ASCII characters correctly. For example, codepoint U+1F601 GRINNING FACE WITH SMILING EYES is wide chars $D83D $DE01 in UTF-16:

    var
      Text: WideString;
    
    Text := 'hi ' + #$D83D#$DE01; // 'hi 😁'
    SendMessage('@channel', Text, 'Markup', False);
    

    Alternatively, you can use HTML in your text messages, so you can encode non-ASCII characters using numerical HTML entities. According to the docs:

    All numerical HTML entities are supported.

    Codepoint U+1F601 is numeric entity $#128513; in HTML:

    var
      Text: WideString;
    
    Text := 'hi $#128513;'; // 'hi 😁'
    SendMessage('@channel', Text, 'HTML', False);