web-dev-qa-db-ja.com

Gmail APIを使用してGmailメッセージを取得する方法

私が達成したいこと:


INBOXのGmail APIを使用していて、基本的には私のGMailアカウントに接続して自分のメールを読みたいカテゴリ、および各メッセージの基本情報を取得(タイトル/件名、fromtodate 、および送信者)。

問題:


thisGoogleのサンプルをC#で記述して、自分のニーズに合わせようとしています。 C#またはVb.Netでのソリューションに関係なく。

(Googleはユーザーの国ごとに異なるコード例を表示しているため、そのWebページのコードは、すべての国で同じになるとは限らないことに注意してください。Googleのロジックは本当にうんざりです。)

以下のコードで私が持っている問題はこれらです:

  • lblInbox.MessagesTotalプロパティで空の値を取得しています。
  • msgItem.Rawプロパティも常に空です。
  • INBOXカテゴリ内のメッセージのみを解析する方法はまだ発見していません。
  • メッセージが既読か未読かを判断する方法はまだわかりません。
  • メッセージの基本情報(件名、差出人、宛先、日付、送信者)を判別する方法はまだわかりません。

これは私が試したものであり、Googleのサンプルを適応させるとき、"user"引数はGmailユーザーアカウント名("[email protected]")であると想定していましたが、それがそうであるかどうかはわかりません。

Imports System.Collections.Generic
Imports System.IO
Imports System.Linq
Imports System.Text
Imports System.Threading
Imports System.Threading.Tasks

Imports Google.Apis.Auth.OAuth2
Imports Google.Apis.Services
Imports Google.Apis.Util.Store
Imports Google.Apis.Gmail
Imports Google.Apis.Gmail.v1
Imports Google.Apis.Gmail.v1.Data
Imports Google.Apis.Gmail.v1.UsersResource

Public Class Form1 : Inherits Form

    Private Async Sub Test() Handles MyBase.Shown
        Await GmailTest()
    End Sub

    Public Async Function GmailTest() As Task
        Dim credential As UserCredential
        Using stream As New FileStream("C:\GoogleAPIKey.json", FileMode.Open, FileAccess.Read)
            credential = Await GoogleWebAuthorizationBroker.AuthorizeAsync(GoogleClientSecrets.Load(stream).Secrets,
                                                                           {GmailService.Scope.MailGoogleCom},
                                                                           "[email protected]",
                                                                           CancellationToken.None)
        End Using

        ' Create the service.
        Dim service As New GmailService(New BaseClientService.Initializer() With {
             .HttpClientInitializer = credential,
             .ApplicationName = "What I need to put here?"
        })

        ' Get the "INBOX" label/category.
        Dim lblReq As UsersResource.LabelsResource.ListRequest = service.Users.Labels.List("me")
        Dim lblInbox As Data.Label = lblReq.Execute().Labels.Where(Function(lbl) lbl.Name = "INBOX").Single
        Dim msgCount As Integer? = lblInbox.MessagesTotal

        MsgBox("Messages Count: " & msgCount)

        If (msgCount <> 0) Then

            ' Define message parameters of request.
            Dim msgReq As UsersResource.MessagesResource.ListRequest = service.Users.Messages.List("me")

            ' List messages of INBOX category.
            Dim messages As IList(Of Data.Message) = msgReq.Execute().Messages
            Console.WriteLine("Messages:")
            If (messages IsNot Nothing) AndAlso (messages.Count > 0) Then
                For Each msgItem As Data.Message In messages
                    MsgBox(msgItem.Raw)
                Next
            End If

        End If

    End Function

End Class

質問:


私は最も重要なニーズを求めます(ただし、他の言及された問題を解決するための助けは大歓迎です):

  • C#またはVB.Netでは、コレクションを取得して、INBOXグループにある電子メールを反復allする方法を教えてください。

更新:

これは私が現在使用しているコードです。意図は、指定されたメールボックスlabelのすべてのMessagesのコレクションを取得することです。問題は、 PayloadオブジェクトのBodyおよびnewMsgメンバーがnullなので、メールを読むことができません。

私は何を間違っているのですか?.

Public Async Function GetMessages(ByVal folder As Global.Google.Apis.Gmail.v1.Data.Label) As Task(Of List(Of Global.Google.Apis.Gmail.v1.Data.Message))

    If Not (Me.isAuthorizedB) Then
        Throw New InvalidOperationException(Me.authExceptionMessage)
    Else
        Dim msgsRequest As UsersResource.MessagesResource.ListRequest = Me.client.Users.Messages.List("me")
        With msgsRequest
            .LabelIds = New Repeatable(Of String)({folder.Id})
            .MaxResults = 50
            '.Key = "YOUR API KEY"
        End With

        Dim msgsResponse As ListMessagesResponse = Await msgsRequest.ExecuteAsync()

        Dim messages As New List(Of Global.Google.Apis.Gmail.v1.Data.Message)
        Do While True

            For Each msg As Global.Google.Apis.Gmail.v1.Data.Message In msgsResponse.Messages
                Dim msgRequest As UsersResource.MessagesResource.GetRequest = Me.client.Users.Messages.Get("me", msg.Id)
                msgRequest.Format = MessagesResource.GetRequest.FormatEnum.Full

                Dim newMsg As Message = Await msgRequest.ExecuteAsync()
                messages.Add(newMsg)
            Next msg

            If Not String.IsNullOrEmpty(msgsResponse.NextPageToken) Then
                msgsRequest.PageToken = msgsResponse.NextPageToken
                msgsResponse = Await msgsRequest.ExecuteAsync()
            Else
                Exit Do
            End If

        Loop

        Return messages

    End If

End Function
9
ElektroStudios

現在、何らかの理由または別の理由で、多くのプロパティがリクエストからnullを返しています。メールIDのリストがあれば、問題を回避できます。次に、これらの電子メールIDを使用して、fromdatesubject、およびbodyの詳細を取得するための別のリクエストを送信できます。 @ DalmTo も正しい方向に進んでいましたが、ヘッダーが最近変更されたため、いくつかのリクエストが必要になるため、ヘッダーについては十分に接近していません。

private async Task getEmails()
{
    try
    {
        UserCredential credential;
        using (var stream = new FileStream("client_secrets.json", FileMode.Open, FileAccess.Read))
        {
            credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
                GoogleClientSecrets.Load(stream).Secrets,
                // This OAuth 2.0 access scope allows for read-only access to the authenticated 
                // user's account, but not other types of account access.
                new[] { GmailService.Scope.GmailReadonly, GmailService.Scope.MailGoogleCom, GmailService.Scope.GmailModify },
                "NAME OF ACCOUNT NOT EMAIL ADDRESS",
                CancellationToken.None,
                new FileDataStore(this.GetType().ToString())
            );
        }

        var gmailService = new GmailService(new BaseClientService.Initializer()
        {
            HttpClientInitializer = credential,
            ApplicationName = this.GetType().ToString()
        });

        var emailListRequest = gmailService.Users.Messages.List("EMAILADDRESSHERE");
        emailListRequest.LabelIds = "INBOX";
        emailListRequest.IncludeSpamTrash = false;
        //emailListRequest.Q = "is:unread"; // This was added because I only wanted unread emails...

        // Get our emails
        var emailListResponse = await emailListRequest.ExecuteAsync();

        if (emailListResponse != null && emailListResponse.Messages != null)
        {
            // Loop through each email and get what fields you want...
            foreach (var email in emailListResponse.Messages)
            {
                var emailInfoRequest = gmailService.Users.Messages.Get("EMAIL ADDRESS HERE", email.Id);
                // Make another request for that email id...
                var emailInfoResponse = await emailInfoRequest.ExecuteAsync();

                if (emailInfoResponse != null)
                {
                    String from = "";
                    String date = "";
                    String subject = "";
                    String body = "";
                    // Loop through the headers and get the fields we need...
                    foreach (var mParts in emailInfoResponse.Payload.Headers)
                    {
                        if (mParts.Name == "Date")
                        {
                            date = mParts.Value; 
                        }
                        else if(mParts.Name == "From" )
                        {
                            from = mParts.Value;
                        }
                        else if (mParts.Name == "Subject")
                        {
                            subject = mParts.Value;
                        }

                        if (date != "" && from != "")
                        {
                            if (emailInfoResponse.Payload.Parts == null && emailInfoResponse.Payload.Body != null)
                            {
                                body = emailInfoResponse.Payload.Body.Data;
                            }
                            else
                            {
                                body = getNestedParts(emailInfoResponse.Payload.Parts, "");
                            }
                            // Need to replace some characters as the data for the email's body is base64
                            String codedBody = body.Replace("-", "+");
                            codedBody = codedBody.Replace("_", "/");
                            byte[] data = Convert.FromBase64String(codedBody);
                            body = Encoding.UTF8.GetString(data);                               

                            // Now you have the data you want...                         
                        }
                    }
                }                    
            }
        }           
    }
    catch (Exception)
    {
        MessageBox.Show("Failed to get messages!", "Failed Messages!", MessageBoxButtons.OK); 
    }
}

static String getNestedParts(IList<MessagePart> part, string curr)
{
    string str = curr;
    if (part == null)
    {
        return str;
    }
    else
    {
        foreach (var parts in part)
        {
            if (parts.Parts  == null)
            {
                if (parts.Body != null && parts.Body.Data != null)
                {
                    str += parts.Body.Data;
                }
            }
            else
            {
                return getNestedParts(parts.Parts, str);
            }
        }

        return str;
    }        
}

現在、このメソッドはすべてのメールIDを取得し、各メールIDについて、各メールのsubjectfromdateおよびbodyを取得します。メソッド全体にコメントがあります。わからないことがありましたらお知らせください。別のメモ:これは回答として投稿する前にもう一度テストされました

25
Çöđěxěŕ

申し訳ありませんが、これは回答ではありません。Zagglerの回答にコメントを追加することはできません(参加したばかりです)。新しい回答として投稿するだけです。Zagglerの回答は非常に優れていますが、小さな問題があります。メール本文に複数の部分がある場合。 Convert.FromBase64 .....は、2つの結合されたbase64文字列では機能しません。したがって、例外が発生します。変換してからボディパーツを結合します。

コードを要求する人もいます。完成したテスト済みコードを次に示します。それらのほとんどはザグラーからコピーされますが、いくつかの例外があります。だから私は上記の問題にたどり着きました。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.IO;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Gmail.v1;
using Google.Apis.Gmail.v1.Data;
using Google.Apis.Services;
using Google.Apis.Util.Store;

namespace GmailTests
{
    class Program
    {
        // If modifying these scopes, delete your previously saved credentials
        // at ~/.credentials/gmail-dotnet-quickstart.json
        static string[] Scopes = { GmailService.Scope.GmailModify };
        static string ApplicationName = "Gmail API .NET Quickstart";

        static void Main(string[] args)
        {
            UserCredential credential;

            using (var stream =
                new FileStream("client_secret.json", FileMode.Open, FileAccess.Read))
            {
                string credPath = System.Environment.GetFolderPath(
                    System.Environment.SpecialFolder.Personal);
                credPath = Path.Combine(credPath, ".credentials/gmail-dotnet-quickstart2.json");

                credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
                    GoogleClientSecrets.Load(stream).Secrets,
                    Scopes,
                    "user",
                    CancellationToken.None,
                    new FileDataStore(credPath, true)).Result;
                Console.WriteLine("Credential file saved to: " + credPath);
            }

            // Create Gmail API service.
            var service = new GmailService(new BaseClientService.Initializer()
            {
                HttpClientInitializer = credential,
                ApplicationName = ApplicationName,
            });


            var re = service.Users.Messages.List("me");
            re.LabelIds = "INBOX";
            re.Q = "is:unread"; //only get unread;

            var res = re.Execute();

            if (res != null && res.Messages != null)
            {
                Console.WriteLine("there are {0} emails. press any key to continue!", res.Messages.Count);
                Console.ReadKey();

                foreach (var email in res.Messages)
                {
                    var emailInfoReq = service.Users.Messages.Get("me", email.Id);
                    var emailInfoResponse = emailInfoReq.Execute();

                    if (emailInfoResponse != null)
                    {
                        String from = "";
                        String date = "";
                        String subject = "";
                        String body = "";
                        //loop through the headers and get the fields we need...
                        foreach (var mParts in emailInfoResponse.Payload.Headers)
                        {
                            if (mParts.Name == "Date")
                            {
                                date = mParts.Value;
                            }
                            else if (mParts.Name == "From")
                            {
                                from = mParts.Value;
                            }
                            else if (mParts.Name == "Subject")
                            {
                                subject = mParts.Value;
                            }

                            if (date != "" && from != "")
                            {
                                if (emailInfoResponse.Payload.Parts == null && emailInfoResponse.Payload.Body != null)
                                    body = DecodeBase64String(emailInfoResponse.Payload.Body.Data);
                                else
                                    body = GetNestedBodyParts(emailInfoResponse.Payload.Parts, "");

                                //now you have the data you want....

                            }

                        }

                        //Console.Write(body);
                        Console.WriteLine("{0}  --  {1}  -- {2}", subject, date, email.Id);
                        Console.ReadKey();
                    }
                }
            }
        }

        static String DecodeBase64String(string s)
        {
            var ts = s.Replace("-", "+");
            ts = ts.Replace("_", "/");
            var bc = Convert.FromBase64String(ts);
            var tts = Encoding.UTF8.GetString(bc);

            return tts;
        }

        static String GetNestedBodyParts(IList<MessagePart> part, string curr)
        {
            string str = curr;
            if (part == null)
            {
                return str;
            }
            else
            {
                foreach (var parts in part)
                {
                    if (parts.Parts == null)
                    {
                        if (parts.Body != null && parts.Body.Data != null)
                        {
                            var ts = DecodeBase64String(parts.Body.Data);
                            str += ts;
                        }
                    }
                    else
                    {
                        return GetNestedBodyParts(parts.Parts, str);
                    }
                }

                return str;
            }
        }
    }
}
11
YanXi

まず、@ codexerの回答に賛成投票します。

次に、彼のコードで次の関数を使用して、base64URLでエンコードされた本文をデコードします。 Googleはbase64で本体をエンコードしただけでなく、URLエンコードも行っています:-/

/// <summary>
    /// Turn a URL encoded base64 encoded string into readable UTF-8 string.
    /// </summary>
    /// <param name="sInput">base64 URL ENCODED string.</param>
    /// <returns>UTF-8 formatted string</returns>
    private string DecodeURLEncodedBase64EncodedString(string sInput)
    {
        string sBase46codedBody = sInput.Replace("-", "+").Replace("_", "/").Replace("=", String.Empty);  //get rid of URL encoding, and pull any current padding off.
        string sPaddedBase46codedBody = sBase46codedBody.PadRight(sBase46codedBody.Length + (4 - sBase46codedBody.Length % 4) % 4, '=');  //re-pad the string so it is correct length.
        byte[] data = Convert.FromBase64String(sPaddedBase46codedBody);
        return Encoding.UTF8.GetString(data);
    }
2
mike

GoogleWebAuthorizationBroker.AuthorizeAsyncのユーザーパラメータは、資格情報を保存するためにFileDatastoreで使用されます。詳細については、チュートリアル Google .net – FileDatastore demystified を確認してください。

私のVB.netは6年間のように非常に錆びていますが、C#では次のようなことができます

UsersResource.MessagesResource.ListRequest request = service.Users.Messages.List("Users email address");
var response = request.Execute();

foreach (var item in response.Messages) {
     Console.WriteLine(item.Payload.Headers);            
 }

MessageResource.ListRequestは、ループできるメッセージオブジェクトのリストを返します。

sers.Messages には、件名とtoおよびfromが含まれるヘッダーが含まれています。

私は gmail に関する非常に古いC#チュートリアルもあるので、役立つかもしれません。

あなたのアップデートに答えるためのアップデート:

削除するとどうなりますか:

.LabelIds = New Repeatable(Of String)({folder.Id})

labelIds string指定されたすべてのラベルIDに一致するラベルを持つメッセージのみを返します。

フォルダーIDを送信しているようです。 ser.lables.list を使用してみてください。これにより、ユーザーのメールボックス内のすべてのラベルがリストされます

2
DaImTo
UsersResource.MessagesResource.GetRequest getReq = null;
Google.Apis.Gmail.v1.Data.Message msg = null;
getReq = gmailServiceObj.Users.Messages.Get(userEmail, MessageID);
getReq.Format = UsersResource.MessagesResource.GetRequest.FormatEnum.Raw;
msg = getReq.Execute();
string converted = msg.Raw.Replace('-', '+');
converted = converted.Replace('_', '/');

byte[] decodedByte = Convert.FromBase64String(converted);
converted = null;
f_Path = Path.Combine(m_CloudParmsObj.m_strDestinationPath,MessageID + ".eml");

if (!Directory.Exists(m_CloudParmsObj.m_strDestinationPath))
    Directory.CreateDirectory(m_CloudParmsObj.m_strDestinationPath);

// Create eml file
File.WriteAllBytes(f_Path, decodedByte);

このようなすべてのメッセージプロパティを含む.emlファイルを取得できます。

1
Renuka