原生推特(Twitter)客户端:第二部

Soewono Effendi | 21 九月, 2020

概述

就像我在第一篇文章“无需 DLL 的 MT4/MT5 的原生推特(Twitter)客户端”中所承诺的那样;本文将尝试探索 Twitter API,从而能发送带有照片的推文。 为了令本文更容易理解,我仅关注上传图像。 到本文结尾,您应能得到一个可运行的推特客户端,且无需用到任何外部 DLL,这令您可发布最多四张照片的消息。 4 张照片的限制是由 Twitter API 设置,如参数 media_ids 中所述。


上传照片

有一种新方法,称为分块上传,在上传媒体时可用更好的方法来上传较大的文件,譬如视频或 GIF 动画。 出于我们的目的,我将关注简单方法,该方法仅限于仅上传图片。

请确保您已熟知推特的媒体类型和大小限制

将照片上传到推特只是一个简单的基本 OAuth 授权 HTTP multipart/form-data POST,我将在下个段落中对其进行解释。 已上传的每张照片都将返回一个 media_id,该 ID 仅在一定时间内有效,从而可在要发布的推文里包含它。

为了发布多达四张照片,所有返回的 media_id 都被简单地连接在一起,形成以逗号分隔的列表。

发布带照片推文的步骤如下:

  1. 上传照片并收集返回的 media_id
  2. 重复上传更多照片,直达上限。 4 张照片。 始终收集其返回的 media_id。
  3. 准备您的推文消息
  4. 发送推文时,请指定 media_id,并在列表里以逗号分隔所有要包含的 media_id。
注意:
为了令代码简单易懂,省略了错误处理。


HTTP multipart/form-data

为了将照片上传到推特,可将这些照片作为原始二进制数据或 Base64 编码的字符串上传。 建议将照片作为原始二进制数据上传,因为 Base64 编码的字符串的大小约暴涨三倍。

HTTP multipart/form-data 方法在 RFC-2388 中加以良好定义,但阅读本篇不错指南也许更容易理解。 基本上,从提到的文章中引用:“这是一个 HTTP POST 请求,其发送的请求主体已特别格式化为一个 "parts" 序列,并用 MIME 边界分隔。”

POST /submit.cgi HTTP/1.1
Host: example.com
User-Agent: curl/7.46.0
Accept: */*
Content-Length: 313
Expect: 100-continue
Content-Type: multipart/form-data; boundary=------------------------d74496d66958873e

--------------------------d74496d66958873e
Content-Disposition: form-data; name="person"

anonymous
--------------------------d74496d66958873e
Content-Disposition: form-data; name="secret"; filename="file.txt"
Content-Type: text/plain

contents of the file
--------------------------d74496d66958873e--


Twitter 类的实现如下:

   void              appendPhoto(string filename, string hash, uchar &data[],
                                 bool common_flag=false)
     {
      int flags=FILE_READ|FILE_BIN|FILE_SHARE_WRITE|FILE_SHARE_READ;
      if(common_flag)
         flags|=FILE_COMMON;
      //---
      int handle=FileOpen(filename,flags);
      if(handle==INVALID_HANDLE)
         return;
      //---
      int size=(int)FileSize(handle);
      uchar img[];
      ArrayResize(img,size);
      FileReadArray(handle,img,0,size);
      FileClose(handle);
      int pos = ArraySize(data);
      int offset = pos + size;
      int hlen = StringLen(hash)+6;
      int newlen = offset + hlen;
      ArrayResize(data, newlen);
      ArrayCopy(data, img, pos);
      StringToCharArray("\r\n--"+hash+"\r\n", data, offset, hlen);
     }

上面的代码将图像文件的原始二进制数据添加到 HTTP multipart/form-data post 的 “parts” 里。 POST 的 “envelope” 的代码完成如下,并指定了 Twitter Upload-API 参数 “media”。

   string              uploadPhoto(string filename)
     {
      // POST multipart/form-data
      string url = "https://upload.twitter.com/1.1/media/upload.json";
      //string url = "https://httpbin.org/anything";
      string params[][2];
      string query = oauthRequest(params, url, "POST");
      string o = getOauth(params);
      string custom_headers = "Content-Type: multipart/form-data;"
                              " boundary=";
      string boundary = getNonce();
      StringAdd(custom_headers, boundary); // use nonce as boundary string
      StringAdd(custom_headers, "\r\n");
      string headers = getHeaders(o, custom_headers, "upload.twitter.com");

      //string query = getQuery(params, url);
      //Print(query);
      uchar data[];
      string part = "\r\n--";
      StringAdd(part, boundary);
      StringAdd(part, "\r\nContent-Disposition: form-data;"
                " name=\"media\"\r\n\r\n");
      StringToCharArray(part, data, 0, StringLen(part));
      appendPhoto(filename, boundary, data);
      string resp = SendRequest("POST", url, data, headers);;
      if(m_verbose)
        {
         SaveToFile(filename + "_post.txt", data);
         Print(resp);
        }
      return (getTokenValue(resp, "media_id"));
     }

为了检查和验证构建的 HTTP multipart/form-data,把 HTTP 请求另外保存在 MT 终端的数据文件夹中,以便进行进一步检查。


带照片的推文

出于简洁起见,我用了一个简单的函数 getTokenValue() 来提取 Twitter Upload-API 返回的 media_id。 您可能会考虑使用出色的 MQL5.com 上提供的 JSON 库。 

以下代码展示了一种非常简单使用 Twitter 类的方法。 函数 Screenshots() 简单地捕获当前打开图表的屏幕截图,并构建一条简单的推文消息。 最多能选择四个图表。
每个屏幕截图均保存到文件,其文件名在字符串数组 fnames 里返回。

屏幕截图被逐一上传,收集返回的 media_id,合并的列表则以逗号分隔。
依据上述逗号分隔列表中指定的 media_ids 参数,我们发布带有这些屏幕截图的推文消息,并将这些屏幕截图附加到推文里。

就是如此容易。

   CTwitter tw(consumer_key, consumer_secret,
               access_token, access_secret, verbose);

   // Gather information
   string fnames[4];
   string msg;
   Screenshots(fnames, msg);

   // Upload screenshots
   int n = ArraySize(fnames);
   int i = n - 1;
   // create comma separated media_ids
   string medias = tw.uploadPhoto(fnames[i]);
   for(i= n - 2; i>=0; i--)
   {
      StringAdd(medias, ",");
      StringAdd(medias, tw.uploadPhoto(fnames[i]));
   }
   
   // Send Tweet with photos' ids
   string resp = tw.tweet(msg, medias);
   Print(resp);


Twitter 类

您可以在 Twitter.mqh 中找到 Twitter 类,其目标是独立于其他包含文件。 若要发送带有照片的推文,这一个文件就能满足您全部需求了。

首先,实例化一个 Twitter 对象,指定您的使用者和访问令牌,并带有一个可选的冗长标志,这有助于在开发过程中进行调试。

   CTwitter tw(consumer_key, consumer_secret,
               access_token, access_secret, verbose);

然后,您可以尝试调用可用的公开函数:

还有一些辅助函数:

  • getTokenValue()
    从 json 字符串返回令牌/参数的值。
    注意: 这是一个非常简单的字符串解析,不要期望能与 json 完全兼容。
  • unquote()
    从字符串中删除引号。


在推特上发布您的图表

附件是一个有效的 MT5 脚本,该脚本可捕获多达四个图表的屏幕截图,并构建一条简单的推文消息,其中包含图表的品种和 OHLCV 值。
这是一个简单的示例,是您开始自行研发智能系统和/或脚本的起点。
注意:

您必须指定自己的使用者、访问令牌和密匙。

以下是脚本发送的推文示例。

自 MT5 发送的带照片推文

图例 1. 自 MT5 发送的带照片推文


推特上的完整 MT5 图表

图例 2. 推特上的全尺寸 MT5 图表 


当然,您也可以附加任何照片;)

幸运猫推文

图例 3. 幸运猫推文


结束语

此处为您提供了一个简单易用的 Twitter 类作为自包含文件,令您轻松发布图表和信号。 还示意了相关的技术细节,希望它们可以很容易就理解。

这个 Twitter 类还远未完工,还有许多其他 Twitter API 会添加到该类之中。 请随时在评论中发表您的改进意见,这都会令 MQL5 社区受益。

我希望您阅读本文时能感到愉悦,就像我写这篇文章时一样。
我也希望您可以用所提供的代码来得到乐趣和收益。

尽享快乐。