English Русский 中文 Español Deutsch Português
Google サービスによるメーリング キャンペーンの手配

Google サービスによるメーリング キャンペーンの手配

MetaTrader 5トレーディング | 2 9月 2019, 08:50
1 131 0
Andrei Novichkov
Andrei Novichkov

イントロダクション

トレーダーは、他のトレーダー、クライアントや友人とのビジネス関係を維持するために、メーリングキャンペーンを手配したい場合があるかもしれません。 また、スクリーンショット、ログ、またはレポートを送信する必要がある場合があります。 頻繁に発生するタスクではないかもしれませんが、このような機能があれば明らかに利点となります。 MQLは便利ではありますが、それだけで実現するのは不可能です。 この記事の最後に、このタスクを解決するために排他的な MQL ツールを使用する問題に戻ります。 ここでは、MQL と C# の組み合わせを使用します。 これにより、必要なコードを比較的記述し、ターミナルに接続できます。 また、この接続に関連する興味深い課題を設定します。

この記事は、ライブラリの作成とターミナルとの統合に関する知識を深め、Google サービスに精通したい初心者や中堅の開発者を対象とします。

タスクの設定

次に、何を行うかをより正確に定義してみましょう。 連絡先のアップデータ可能なリストがあり、ユーザーが一覧から任意の連絡先に添付ファイルを 1 回または繰り返し送信できます。 考慮すべき事項:

  • リストの特定の連絡先にアドレスがないか、正しくない可能性があります。 また、複数のアドレスが存在する場合もあります。
  • リストを変更できます 。
  • 連絡先を複製できます。
  • また、リストに残っている間は、メーリング キャンペーンから除外される場合もあります。 つまり、連絡先のアクティビティは調整可能である必要があります。
  • さらに、このリストには、問題のタスクとは無関係の連絡先が含まれます。

リスト管理の実装は、最も明白なタスクです。 どんな選択肢があるでしょうか?

  1. HDD データベースまたは CSV ファイルは不便で、十分な信頼性がありません。 常に利用できるわけではないため、このようなストレージを管理するために追加のソフトウェアが必要になる場合があります。
  2. Joomla型CMSをフィーチャーした特設サイトのデータベース。 これは良いソリューションです。 データはどこからでも保護され、アクセスできます。 また、電子メールは、ウェブサイトから送信することができます。 ただし、大きな欠点もあります。 このような Web サイトとやり取りするには、特別なアドオンが必要です。 このようなアドオンは大きく、セキュリティホールで謎に包まれている可能性があります。 言い換えれば、信頼性の高いインフラストラクチャが必要です。
  3. そこで既製の Google サービスを使用します。 そこでは、連絡先を安全に保存および管理し、異なるデバイスから連絡先にアクセスできます。 特に、さまざまなリスト (グループ) を形成し、電子メールを送信できます。 快適なタスクに必要なのはだけです。 では、この方法をみていきましょう。

Googleとのデータのやり取りは、例えばここなどに、多くのドキュメンテーションがあります。 Google でのタスクを開始するには、アカウントを登録し、連絡先の一覧を作成します。 このリストには、電子メールを送信する連絡先が含まれている必要があります。 連絡先で、特定の名前のグループ (例: "外国為替" など) を作成し、選択した連絡先を追加します。 各連絡先は、後で使用できるように複数のデータを保存できます。 残念ながら、ユーザーがまだ追加のデータ フィールドを必要とする場合は、作成できません。 使用可能なデータ フィールドが多いため、不便を引き起こしません。 後で使用する方法を示します。

次に、メイン タスクに進みます。

Google側での準備

すでに Google アカウントがあるとします。 Google "コンソールの開発"を使用してプロジェクト開発を再開します。 ここでは、コンソールの使用方法とプロジェクトの開発方法について詳しく説明します。 もちろん、上記の記事は別のプロジェクトについて説明します。 プロジェクトには名前が必要です。 " WorkWithPeople"にしましょう。 他のサービスが必要です。 この段階で、次のものを有効にします。

  • People API
  • Gmail API

最初のは、連絡先のリストへのアクセスを提供します(実際には、他のものへのアクセスも提供しますが、必要なのはリストだけです)。 連絡先のリストにアクセスするための別のサービスがあります — Contacts APIですが、現時点では使用はお勧めしません。

名前が示唆するように、2 番目のサービスはメールへのアクセスを提供します。

サービスを有効にし、アプリケーションにアクセスを許可するキーを取得します。 書き留めたり覚えたりする必要はありません。 キーを含む Google リソースへのアクセスに必要なすべてのデータを含む json 形式で添付ファイルをダウンロードします。 ファイルをディスクに保存し、より意味のある名前を付けます。 "WorkWithPeople_gmail.json"としましょう。 これで、Googleとの直接的なタスクを完了します。 アカウント、連絡先リスト、プロジェクトを作成し、アクセスファイルを作成しました。

次に、VS 2017 のタスクに進みましょう。

プロジェクトとパッケージ

VS 2017 を開き、標準Class Library (.NET Framework)プロジェクトを作成します。 任意の覚え可能な方法で名前を付けます (必須ではありませんが、Google プロジェクト名 " WorkWithPeople"と一致させます)。 NuGetを使用して追加のパッケージをすぐにインストールします。

  • Google.Apis
  • Google.Apis.People.v1
  • Google.Apis.PeopleService.v1
  • Google.Apis.Gmail.v1
  • MimeKit

    インストール中に、NuGet は関連パッケージのインストールを提供します。 同意します。 この場合、プロジェクトは連絡先を操作し、メールを管理するための Google パッケージを受け取ります。 これで、コードを開発する準備が整いました。

    連絡先へのアクセス

    補助クラスから始めましょう。 特定の Google 連絡先に含まれるデータの量を考慮すると、その主要な部分がタスクに必要でないことが明らかになります。 メールを送信するには、連絡先名と住所が必要です。 実際には、さらに別のフィールドからのデータが必要ですが、後で詳しく説明する必要があります。

    クラスは次のようになります。

    namespace WorkWithPeople
    {    
        internal sealed class OneContact
        {
            public OneContact(string n, string e)
            {
                this.Name  = n;
                this.Email = e;
            }
            public string Name  { get; set; }
            public string Email { get; set; }
        }
    }

    連絡先名とアドレスを格納する "string"型のプロパティと、初期化する 2 つのパラメータを備えたシンプルなコンストラクタがあります。 追加のチェックは実装されません。 他の場所で実行する予定です。

    連絡先リストの読み取り時に、シンプルな要素のリストが作成されます。 この新しく構築されたリストのデータに基づいてメーリングキャンペーンを実施できます。 リストを更新する場合は、すべてのリスト要素を削除し、Google アカウントからデータの読み取りと選択の操作を繰り返します。

    さらに別の補助クラスがあります。 連絡先リストに無効な電子メール アドレスが含まれているか、アドレスがまったく存在しない可能性があります。 電子メールを送信する前に、アドレスが存在し、正しいことを確認する必要があります。 新しい補助クラスを開発してみましょう:

    namespace WorkWithPeople
    {    
        internal static class ValidEmail
        {
            public stati cbool IsValidEmail(this string source) => !string.IsNullOrEmpty(source) && new System.ComponentModel.DataAnnotations.EmailAddressAttribute().IsValid(source);
        }
    }

    チェックを実行するには、使用可能なツールを使用しますが、正規表現も使用できます。 さらなる使用の便宜に、拡張メソッドとしてコードを開発します。 推測することは難しくないため、宛先アドレスを含む文字列がfalseを渡した場合、メソッドは true を返します。 次に、コードの主要な部分に進みます。

    サービスへのアクセスと操作

    既にプロジェクトを作成し、キーを受け取り、アプリケーションを承認するための JSON ファイルをダウンロードしました。 では、新しいクラス ContactsPeople を作成し、適切なアセンブリをファイルに追加してみましょう。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.IO;
    using System.Net.Mail;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    using Google.Apis.Auth.OAuth2;
    using Google.Apis.People.v1;
    using Google.Apis.Services;
    using Google.Apis.Util.Store;
    using Google.Apis.Http;
    using Google.Apis.PeopleService.v1;
    using Google.Apis.PeopleService.v1.Data;
    using Google.Apis.Gmail.v1;
    using Google.Apis.Gmail.v1.Data;
    
    namespace WorkWithPeople
    {
        internal sealed class ContactsPeople
        {
           public static string Applicationname { get; } = "WorkWithPeople";
    
    .....

    Google プロジェクト名を含む静的プロパティを追加します。この静的プロパティは読み取り専用にされます。

    閉じたフィールドと列挙体をクラスに追加します。

            private enum             PersonStatus
            {
                Active,
                Passive
            };
            private string           _groupsresourcename;
            private List<OneContact> _list = new List<OneContact>();
            private UserCredential   _credential;
            private PeopleService    _pservice;
            private GmailService     _gservice;

    列挙体は、連絡先を "active" (電子メールを受信する) と "passive(パッシブ)" (電子メールを受信しない) としてマークするために使用します。 その他の閉じたフィールド:

    • _groupsresourcename. "連絡先"で作成されたグループに対応する Google リソース名。 (今回の場合、選択したグループ名は"Forex"でした)。
    • _list. このメーリングキャンペーンの連絡先の一覧は、適用されます。
    • _credential. Application "powers".
    • _pservice, _gservice. 連絡先やメールを操作するためのサービス。

    主なタスク関数のコードを記述してみましょう。

            publicint WorkWithGoogle(string credentialfile, 
                                       string user, 
                                       string filedatastore, 
                                       string groupname,
                                       string subject,
                                       string body,
                                       bool   isHtml,
                                       List<string> attach = null)
            {
              ...

    その引数は次のとおりです。

    • credentialfile. サービスにアクセスするためのすべてのデータを含む JSON ファイルにアクセスする名前とパス。 以前、 Google アカウントからダウンロードされました。
    • user. Google account name — XXXXX@gmail.com address.
    • filedatastore. 補助フォルダの名前 — ユーザーの PC 上のストレージ (任意の場合があります)。 フォルダは AppData 内に作成されます (%APPDATA%)追加のアクセス データを含むファイルが含まれます。
    • groupname. 作成したメーリング キャンペーンの連絡先グループの名前。 この場合、"Forex"です。
    • subject, body, isHtml. 電子メールの件名とテキスト、および HTML 形式で書かれているかどうか。
    • attach. 添付ファイルの一覧。

    Returned value — 送信された電子メールの数。 関数コードの記述を開始します。

                if (!File.Exists(credentialfile))
                    throw (new FileNotFoundException("Not found: " + credentialfile));
                using (var stream = new FileStream(credentialfile, FileMode.Open, FileAccess.Read))
                {
                    if (_credential == null) {
                        _credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
                            GoogleClientSecrets.Load(stream).Secrets,
                            new[]
                            {
                                GmailService.Scope.GmailSend,
                                PeopleService.Scope.ContactsReadonly
                            },
                            user,
                            CancellationToken.None,
                            new FileDataStore(filedatastore)).Result;
                            CreateServicies();
                    }
                    else if (_credential.Token.IsExpired(Google.Apis.Util.SystemClock.Default)) {
                        bool refreshResult = _credential.RefreshTokenAsync(CancellationToken.None).Result;
                        _list.Clear();
                        if (!refreshResult) return 0;   
                        CreateServicies();
                    }
                    
                }// using (var stream = new FileStream(credentialfile, FileMode.Open, FileAccess.Read))

    リクエストするサービスへのアクセスを定義する文字列の配列に注意してください。

    • GmailService.Scope.GmailSend. 電子メールを送信するためのアクセス。
    • PeopleService.Scope.ContactsReadonly. 読み取り専用モードでの連絡先へのアクセス。

    また、GoogleWebAuthorizationBroker.AuthorizeAsyncを呼び出すメモ. その名前は、呼び出しが非同期的に実行されることを示唆します。

    以前に受け取ったトークンが期限切れの場合、コードは更新し、以前に形成された _list からすべてのオブジェクトを削除します。

    補助的な CreateServicies() 関数は、必要なオブジェクトを作成および初期化します。

            private void         CreateServicies()
            {
                _pservice = new PeopleService(new BaseClientService.Initializer()
                {
                    HttpClientInitializer = _credential,
                    ApplicationName = Applicationname
                });
                _gservice = new GmailService(new BaseClientService.Initializer()
                {
                    HttpClientInitializer = _credential,
                    ApplicationName = Applicationname
                });
            }

    ご覧のとおり、上記のコード セグメントを実行すると、必要なサービスにアクセスできます。

     - JSON データ ファイルを使用して、最初に "powers" をリクエストし、_credential フィールドに保存します。 次に、"power" とプロジェクト名フィールドを初期化リストとして渡すサービス コンストラクタを呼び出します。

    メーリングキャンペーンで選択したグループの連絡先リストを取得します。

                try {
                      if (_list.Count == 0)
                        GetPeople(_pservice, null, groupname);                     
                }
                catch (Exception ex) {
                    ex.Data.Add("call GetPeople: ", ex.Message);
                    throw;
                }
    #if DEBUG
                int i = 1;
                FOReach(_listvar nm){
                    Console.WriteLine("{0} {1} {2}", i++, nm.Name, nm.Email);
                }
    #endif
                if (_list.Count == 0) {
                    Console.WriteLine("Sorry, List is empty...");
                    return 0;
                }

    GetPeople(...) 関数 (後述) は、連絡先を格納する _list にインプットすることです。 この関数は例外ソースとして機能するため、そのブロックはtryブロックにラップされます。 接続されたアセンブリでは例外型が検出されないため、 catchブロックは最も一般的な形式で記述されます。 言い換えれば、デバッグの貴重なデータを失わないために、ここで可能なすべての発生をインクルードする必要はありません。 したがって、必要と思うデータを例外に追加し、再アクティブ化します。

    _list は空の場合、つまり新しいトークンを受け取ったり、古いトークンを更新したりする場合にのみ更新されます。

    次のブロックは、デバッグ バージョンに対してのみ実行されます。 形成されたリスト全体がコンソールに表示されます。

    直近のブロックは明白です。 リストが空のままの場合、以降のタスクにはポイントがなく、適切なメッセージが伴ってストップされます。

    この関数は、コード ブロックが送信電子メールを形成し、メーリング キャンペーンを実行することで終了します。

                using (MailMessage mail = new MailMessage
                {
                    Subject = subject,
                    Body = body,
                    IsBodyHtml = isHtml
                })  // MailMessage mail = new MailMessage
                {
                    if (attach != null)
                    {
                        foreach (var path in attach)
                            mail.Attachments.Add(new Attachment(path));
                    } //  if (attach != null)
    
                    foreach (var nm in _list)
                        mail.To.Add(new MailAddress(nm.Email, nm.Name));
                    try
                    {
                        SendOneEmail(_gservice, mail);
                    }
                    catch (Exception ex)
                    {
                        ex.Data.Add("call SendOneEmail: ", ex.Message);
                        throw;
                    }
                }// using (MailMessage mail = new MailMessage

    MailMessage ライブラリ クラスのインスタンスがここで作成されます。 その後、その後の初期化とフィールドへのインプットが続きます。 添付ファイルの一覧が存在する場合は、添付ファイルが追加されます。 最後に、前の段階で得られたメーリングリストが形成されます。

    メーリングは、後述するSendOneEmail(...)関数によって実行されます。 GetPeople(...)関数と同様に、例外ソースになることもあります。 したがって、その呼び出しもtryブロックにラップされ、catchでの処理も同様に行われます。

    この時点で、WorkWithGoogle(...)メイン関数のタスクは完了と見なされ、_list.Count が返されます。電子メール メッセージがリストから各連絡先に送信されたと仮定して、値をカウントします。

    連絡先リストへの記入

    アクセスを取得した後、_list は準備ができています。 これは、この関数によって行われます。

            private void         GetPeople(PeopleService service, string pageToken, string groupName)
            {
               ...

    その引数は次のとおりです。

    • service. 以前に作成した Google の連絡先へのアクセス クラスへのリンク。
    • pageToken. 複数の連絡先が存在する可能性があります。 この引数は、連絡先の一覧が複数のページを占していることを開発者に伝えます。
    • groupName. 関心のある連絡先グループの名前。

    初めて、この関数は pageToken = NULL で呼び出されます。 その後、Google へのリクエストが NULL と異なる値を持つトークンを返した場合、関数は再帰的に呼び出されます。

                if (string.IsNullOrEmpty(_groupsresourcename))
                {
                    ContactGroupsResource groupsResource = new ContactGroupsResource(service);
                    ContactGroupsResource.ListRequest listRequest = groupsResource.List();
                    ListContactGroupsResponse response = listRequest.Execute();
                    _groupsresourcename = (from gr in response.ContactGroups
                                           where string.Equals(groupName.ToUpperInvariant(), gr.FormattedName.ToUpperInvariant())
                                           select gr.ResourceName).Single();
                    if (string.IsNullOrEmpty(_groupsresourcename))
                        throw (new MissingFieldException($"Can't find GroupName: {groupName}"));
                }// if (string.IsNullOrEmpty(_groupsresourcename))

    グループ名でリソース名を調べる必要があります。 これを実現するには、すべてのリソースのリストをリクエストし、シンプルなラムダ式で必要なものを見つけます。 必要な名前を持つリソースは 1 つだけにする必要があります。 タスク中にリソースが見つからない場合は、例外が有効になります。

    Google.Apis.PeopleService.v1.PeopleResource.ConnectionsResource.ListRequest peopleRequest =
                    new Google.Apis.PeopleService.v1.PeopleResource.ConnectionsResource.ListRequest(service, "people/me")
                    {
                        PersonFields = "names,emailAddresses,memberships,biographies"
                    };
                if (pageToken != null) {
                    peopleRequest.PageToken = pageToken;
                }

    必要なリストを取得するためにGoogleにリクエストを作成してみましょう。 これを行うには、Google の連絡先データのフィールドを指定します。

    • names, emailAddresses. OneContact クラスのインスタンスを作成します。
    • memberships. 連絡先がグループに属しているかどうかを確認します。
    • biographies. このフィールドは、連絡先の経歴を格納するように設計されていますが、接触アクティビティを管理するために選択されます。 連絡先をアクティブとして認識し、そのアドレスに電子メールを送信するには、フィールドが 1 から始まる必要があります。 それ以外の場合、連絡先はパッシブと見なされ、必要なグループ内にある場合でも無視されます。 そこにこの特定のフィールドを使用する必要はありません。 今回の場合、比較的頻繁に使用するため、選択されます。 特定の連絡先を "有効/無効にする"を可能にするメーリングキャンペーンを管理するユーザーにとって便利です。

    最後に、次のリクエストを行います。

                var request = peopleRequest.Execute();
                var list1 = from person in request.Connections
                         where person.Biographies != null
                         from mem in person.Memberships
                         where string.Equals(_groupsresourcename, mem.ContactGroupMembership.ContactGroupResourceName) &&
                               PersonActive(person.Biographies.FirstOrDefault()?.Value) == PersonStatus.Active
                         let name = person.Names.First().DisplayName
                         orderby name
                         let email = person.EmailAddresses?.FirstOrDefault(p => p.Value.IsValidEmail())?.Value
                         where !string.IsNullOrEmpty(email)
                         select new OneContact(name, email);
                _list.AddRange(list1);
                if (request.NextPageToken != null) {
                    GetPeople(service, request.NextPageToken, groupName);
                }
            }//void GetPeople(PeopleService service, string pageToken, string groupName)

    リクエストを行い、必要なデータをラムダ式で並べ替えます。 かなりかさばって見えますが、実際には簡単です。 連絡先は、ゼロ以外の経歴を持ち、正しいグループに入り、アクティブな連絡先であり、正しいアドレスがある必要があります。 ここでは、単一の連絡先の "アクティブ/パッシブ" ステータスを定義する関数を"biographies" フィールドの内容で示しましょう。

            private PersonStatus PersonActive(string value)
            {
                try {
                    switch (Int32.Parse(value))
                    {
                        case 1:
                            return PersonStatus.Active;
                        default:
                            return PersonStatus.Passive;
                    }
                }
                catch (FormatException)   { return PersonStatus.Passive; }
                catch (OverflowException) { return PersonStatus.Passive; }
            }//PersonStatus PersonActive(string value)

    これは、代わりにその場で例外の一部を処理しようとする例外を再有効にしようとしないプロジェクト内の唯一の関数です。

    もうすぐ終わりです! 取得したリストを _list に追加します。 すべての連絡先が読み取られていない場合は、新しいトークン値で関数を再帰的に呼び出します。

    電子メールの送信

    これは次の補助関数によって実行されます。

            private void SendOneEmail(GmailService service, MailMessage mail)
            {
                MimeKit.MimeMessage mimeMessage = MimeKit.MimeMessage.CreateFromMailMessage(mail);
                var encodedText = Base64UrlEncode(mimeMessage.ToString());
                var message = new Message { Raw = encodedText };
    
                var request = service.Users.Messages.Send(message, "me").Execute();
            }//  bool SendOneEmail(GmailService service, MailMessage mail)

      その呼び出しは上述の通りです。 このシンプルな関数の目的は、メールを送信するための電子メールを準備し、メーリングキャンペーンを実行することです。 また、この関数には、すべての "heavy" 準備操作があります。 残念ながら、Google は MailMessage クラスの形式でデータを受け付けません。 したがって、許容可能な形式でデータを準備し、コーディングします。 MimeKitアセンブリには、コーディングを実行するツールがあります。 しかし、利用できる簡単な関数を使用する方がはるかに簡単でしょう。 そのシンプルさは、ここでは示しません。 service.Users.Messages.Send内の文字列型の特殊なuserIdに注意してください。 特別な"me"値と同じで、Google がアカウントにアクセスして送信者データを取得できます。

      ContactsPeople クラスの分析は終了です。 残りの関数は補助関数なので、その関数に意味がありません。

      ターミナルコネクタ

      残る唯一の問題は、(未完成の) アセンブリをターミナルに接続することです。 一見すると、このタスクは簡単です。 静的メソッドを定義し、プロジェクトをコンパイルし、ターミナルのライブラリフォルダにコピーします。 MQL コードからアセンブリの静的メソッドを呼び出します。 しかし、正確には何をコピーする必要があるでしょうか。 dll ライブラリの形式のアセンブリがあります。 また、NuGetによってダウンロードされた約12のアセンブリは、今回のタスクで積極的に使用します。 Google にアクセスするためのデータを格納する JSON ファイルがあります。 セット全体をライブラリ フォルダにコピーしてみましょう。 プリミティブ MQL スクリプトを作成し (ここでコードをアタッチしても意味がありません)、アセンブリから静的メソッドを呼び出してみてください。 例外があります! Google.Apis.dll が見つかりません。 これは極めて喜ばざる例外であり、CLR はメイン アセンブリと同じフォルダに配置されていますが、目的のアセンブリを見つけることができません。 なぜこんなことが起こっているのでしょうか。 ここで詳しく状況を調べる価値はありません。 興味がある場合は、リヒター(Richter)の有名な著書でこのことについて見つけることができます (プライベートアセンブリの検索に関するセクション).

      MetaTrader で動作する完全に機能する .Net アプリケーションの例は既に多数あります。 このような問題もそこで起こりました。 どのように解決されたのでしょうか。 ここでは、.Net アプリケーションと MQL プログラムの間にチャネルを作成し、 ここでイベント ベースのモデルを使用することで問題を解決しました。 コマンドラインを使用して、MQLプログラムから.Netアプリケーションに必要なデータを渡すことを含む同様のアプローチを提案することができます。

      しかし、より多くの"エレガント"、シンプルで普遍的なソリューションを検討する価値があります。 AppDomain.AssemblyResolveイベントを使用してアセンブリのダウンロードを管理します。 このイベントは、実行要件がアセンブリを名前でバインドできない場合に発生します。 この場合、イベント ハンドラは、別のフォルダ (ハンドラが知っているアドレスを持つ) からアセンブリを読み込んで返すことができます。 したがって、かなり美しいソリューションになります。

      1. "Libraries" フォルダに別の名前を持つフォルダを作成します (今回の場合は"WorkWithPeople"です)。
      2. メソッドを MQL を含むファイルにインポートするアセンブリは、"ライブラリ" フォルダにコピーされます。
      3. Google サービスへのアクセスに関するデータを含む JSON ファイルを含む他のすべてのプロジェクト アセンブリは、"WorkWithPeople" フォルダにコピーされます。
      4. ライブラリ フォルダ内のメイン アセンブリに、他のアセンブリを検索する必要があるアドレス ("WorkWithPeople" フォルダへの完全なパス) を知らせます。

      その結果、"ライブラリ" フォルダを乱雑にすることなく、実行可能なソリューションが得られます。 コード内の決定を実装するだけの場合です。

      制御クラス

      静的クラスを作成してみましょう

          public static class Run
          {
      
              static Run() {
                  AppDomain.CurrentDomain.AssemblyResolve += ResolveAssembly;
              }// Run()

      できるだけ早くハンドラ チェーンに表示されるように、イベント ハンドラを追加します。 ハンドラ自体を定義してみましょう。

              static Assembly ResolveAssembly(object sender, ResolveEventArgs args) {
                  String.dllName = new AssemblyName(args.Name).Name + ".dll";
                  return Assembly.LoadFile(Path.Combine(_path,.dllName) );
              }// static Assembly ResolveAssembly(object sender, ResolveEventArgs args)

      アセンブリが検出されるたびに、このハンドラが呼び出されます。 その目的は、_path 変数 (開始中に定義) と計算された名前からパスを組み合わせたアセンブリをダウンロードして返すことです。 これで、ハンドラがアセンブリを見つけられない場合にのみ例外が表示されます。

      初期化関数は次のようになります。

      public static void Initialize(string Path, string GoogleGroup, string AdminEmail, string Storage)
              {
                  if (string.IsNullOrEmpty(Path) ||
                      string.IsNullOrEmpty(GoogleGroup) ||
                      string.IsNullOrEmpty(AdminEmail) ||
                      string.IsNullOrEmpty(Storage)) throw (new MissingFieldException("Initialize: bad parameters"));
                  _group = GoogleGroup;
                  _user = AdminEmail;
                  _storage = Storage;
                  _path = Path;
              }//  Initialize(string Path, string GoogleGroup, string AdminEmail, string Storage)

      この関数は、電子メールの送信が試行される前に、最初の関数と呼ばれる必要があります。 その引数は次のとおりです。

      • Path. ハンドラがアセンブリを検索するパスと、Google にアクセスするためのデータを含むファイルが配置されているパス。
      • GoogleGroup. メールに使用する連絡先のグループの名前。
      • AdminEmail. メーリングが行われるアカウント名/メールアドレス(XXX@google.com)。
      • Storage. 追加データが格納される補助ファイルの名前。

      記述されたすべての引数は空の文字列であってはなりません。

      含まれるファイルのリストと簡単な追加関数を作成します。

      public static void AddAttachment (string attach) { _attachList.Add(attach);}

      この関数は、MetaTrader環境で予備的に作成されたスクリーンショットやその他のファイルを扱うため、エラーチェックツールを備えています。 ターミナルで動作する制御ツールによって行われることを前提とします。

      すぐにメール用のオブジェクトを作成してみましょう

      static ContactsPeople _cContactsPeople = new ContactsPeople();

      関数を呼び出して実行します。

      public static int DoWork(string subject, string body, bool isHtml = false) {
                  if (string.IsNullOrEmpty(body))
                      throw (new MissingFieldException("Email body null or empty"));
                  int res = 0;
                  if (_attachList.Count > 0) {
                      res = _cContactsPeople.WorkWithGoogle(Path.Combine(_path, "WorkWithPeople_gmail.json"),
                          _user,
                          _storage,
                          _group,
                          subject,
                          body,
                          isHtml,
                          _attachList);
                      _attachList.Clear();
                  }その他{
                      res = _cContactsPeople.WorkWithGoogle(Path.Combine(_path, "WorkWithPeople_gmail.json"),
                          _user,
                          _storage,
                          _group,
                          subject,
                          body,
                          isHtml);
                  }// if (_attachList.Count > 0) ... else ...
                  return res;
              }// static int DoWork(string subject, string body, bool isHtml = false)

      インプットは次のとおりです。

      • subject. 電子メールの件名。
      • body. 電子メールテキスト。
      • isHtml. 電子メールがHTML 形式かどうか。

      電子メールが添付ファイルを備えているかどうかに応じて、_cContactsPeople.WorkWithGoogleを呼び出す 2 つのオプションがあります。 呼び出しの最初の引数は特に興味深いです。

      Path.Combine(_path, "WorkWithPeople_gmail.json")

      これは、Google サービスにアクセスするためのデータを含むファイルへの完全なパスです。

      DoWork(...) 関数は、送信された電子メールの数を返します。

      Googleにアクセスするためのデータファイルを除くVS++ 2017のプロジェクト全体は、添付のgoogle.zipアーカイブにあります。

      メタトレーダー側の準備

      アセンブリ コードの準備ができました。 ターミナルに移り、そこに簡単なスクリプトを作成してみましょう。 次のように記述できます (最初のコードの一部はスキップされます)。

      #import "WorkWithPeople.dll"
      
      
      void OnStart()
        {
         string scr = "scr.gif";
         string fl = TerminalInfoString(TERMINAL_DATA_PATH) + "\\MQL5\\Files\\";
         ChartScreenShot(0, scr, 800, 600);  
         Run::Initialize("e:\\Forex\\RoboForex MetaTrader5 Demo\\MQL5\\Libraries\\WorkWithPeople\\" ,"Forex" ,"ХХХХХХ@gmail.com" ,"WorkWithPeople" );
         Run::AddAttachment(fl + scr);
         int res = Run::DoWork("some subj" ,
                               "Very big body" ,
                                false );
         Print("result: ", res);   
        }

      このコードは明白です。 アセンブリをインポートします。 最初に行うのは、初期化し、以前に作成したスクリーンショットを追加し、メーリングを実行することです。 完全なコードは、添付ファイルgoogle_test1.mq5にあります。

      別の例は、M5 で動作し、新しいロウソクが検出されるたびにスクリーンショットを含む電子メールを送信するインジケータです。

      #import "WorkWithPeople.dll"
      
      input string scr="scr.gif";
      
      string fp;
      
      int OnInit()
        {
         fp=TerminalInfoString(TERMINAL_DATA_PATH)+"\\MQL5\\Files\\";
         Run::Initialize("e:\\Forex\\RoboForex MetaTrader5 Demo\\MQL5\\Libraries\\WorkWithPeople\\","Forex","0ndrei1960@gmail.com","WorkWithPeople");
      
         return(INIT_SUCCEEDED);
        }
      
      int OnCalculate(const int rates_total,
                      const int prev_calculated,
                      const datetime &time[],
                      const double &open[],
                      const double &high[],
                      const double &low[],
                      const double &close[],
                      const long &tick_volume[],
                      const long &volume[],
                      const int &spread[])
      
        {
         if(IsNewCandle()) 
           {
            ChartScreenShot(0,scr,800,600);
            Run::AddAttachment(fp+scr);
            string body="Time: "+TimeToString(TimeLocal());
            int res=Run::DoWork("some subj",body,false);
            Print(body);
           }
         return(rates_total);  
        }

      インジケータの完全なコードは、添付ファイルgoogle_test2.mq5にあります。 簡単ですので、以上のコメントは必要ありません。

      結論

      結果を見てみましょう。 Googleの連絡先を使用してパートナーとやり取りする方法や、不要なファイルを含むフォルダの煩雑さを避けるため、アセンブリをターミナルに統合する方法を分析しました。 アセンブリ コードの効率も言及する価値があります。 ここでは、この問題に十分な注意を払っていませんが、対処するための一連の活動を提供することは可能です。

      • Google での承認とメールの送信の目的を分割します。 タイマーによる別のスレッドでの承認にします。
      • 電子メールの送信にはスレッド プールを使用してみてください。
      • 電子メールの添付ファイルの "heavy" コーディングには、非同期ツールを使用します。

      すべてのメソッドを使用する必要があるという意味ではありませんが、その使用によってパフォーマンスが向上し、結果のアセンブリを MetaTrader と個別に適用できる可能性があります。

      最後に、このタスクを解決するために MQL ツールを使用する問題に戻りましょう。 可能なのでしょうか。 Googleのドキュメントによると、答えはイエスです。 GET/POSTリクエストを使用して同じ結果を達成することができ、適切な例が利用可能です。 したがって、通常のWebRequestを使用できます。 このメソッドの実現可能性は依然として考察の余地があります。 リクエストの数が多いため、このようなコードの作成、デバッグ、および保守は困難です。

      この記事で使用するプログラム

       # 名前
      種別
       詳細
      1 google_test1.mq5
      スクリプト
      スクリーンショットを作成し、複数のアドレスに送信するスクリプト。
      2
      google_test1.mq5 インジケーター
      新しいロウソクごとに電子メールを送信するサンプルインジケータ
      3 google.zip アーカイブ アセンブリおよびテスト コンソール アプリケーション プロジェクト。

      MetaQuotes Ltdによってロシア語から翻訳されました。
      元の記事: https://www.mql5.com/ru/articles/6975

      添付されたファイル |
      google_test1.mq5 (0.95 KB)
      google_test2.mq5 (2.76 KB)
      google.zip (12.71 KB)
      MetaTraderプログラムを簡単かつ迅速に開発するためのライブラリ(第9部): MQL4との互換性 - データの準備 MetaTraderプログラムを簡単かつ迅速に開発するためのライブラリ(第9部): MQL4との互換性 - データの準備
      前の記事では、MetaTrader 5とMetaTrader 4プラットフォーム用のプログラムの開発を単純化するための大規模なクロスプラットフォームライブラリの作成を始めました。第8部では、注文とポジションの変更イベントを追跡するためのクラスを実装しました。ここでは、MQL4と完全な互換性を備えさせることでライブラリを改善します。
      直近のピップのプロフィットダウンを抽出 直近のピップのプロフィットダウンを抽出
      この記事では、アルゴリズムトレード分野における理論と実践を組み合わせる試みについて説明します。 トレーディングシステムの作成に関する考察のほとんどは、ヒストリーバーや適用される様々なインジケータの使用に関連します。 これは最もよくカバーされたフィールドであるため、詳細は考慮しません。 バーは人工的なエンティティを表します。したがって、プロトデータに近い何か、すなわち価格ティックで動作します。
      最適化管理 (パート I): GUI の作成 最適化管理 (パート I): GUI の作成
      この記事では、MetaTrader ターミナルの拡張機能を作成するプロセスについて説明します。 このソリューションは、他のターミナルで最適化を実行する際、最適化プロセスを自動化するのに役立ちます。 このトピックに関する記事をいくつか書きます。 拡張機能は C# 言語とデザイン パターンを使用して開発されました。優先プログラミング言語の機能です。
      クロスプラットフォームグリッドEAの開発(パートIII):マーチンゲールによる補正ベースのグリッド クロスプラットフォームグリッドEAの開発(パートIII):マーチンゲールによる補正ベースのグリッド
      この記事では、可能な限り最高のグリッドベースのEAの開発に取り組みます。 いつものように、MetaTrader4とMetaTrader5の両方で動作することができるクロスプラットフォームEAになります。 当初このEAは、長期間にわたって利益を上げることができませんでしたが、それ以外は良好でした。 2番目となるこのEAは、数年以上にわたって動作する可能性があります。 しかし、残念ながら、最大ドローダウンが50%未満で、年間利益の50%以上の条件は得ることができませんでした。