Статья размещена автором Бетке Сергей Сергеевич

API Яндекс:Почта для домена и PowerShell: скриптуем get_token

Продолжаю небольшую серию статей на тему Почта для домена от Яндекс. Естественно, готовлю сценарии для автоматического создания необходимых ящиков на указанном сервисе. Яндекс предоставил нам API для этих целей. Однако, для всех методов предварительно требуется получить token (API get_token), идентифицирующий Вас и Ваш основной домен. И получить этот token через сценарий оказалось непростой задачей. Решим её!


get_token

Пробовал разные варианты: пытался самостоятельно авторизоваться через WebClient на Яндексе, получить cookies, и уже с ними получить token. Времени убил много, результатов ноль. Да и зачем в скрипте явно указывать учётную запись и пароль?

Решил пойти другим путём – воспользоваться браузером. Преимущества на лицо: cookies (если уже установлена постоянная авторизация) сделают своё дело, если же авторизации нет – администратор авторизуется через интерфейс, предоставленный ему Яндексом. Приведу код:

$domain = 'csm.nov.ru';

Import-Module `
    (join-path `
        -path ( ( [System.IO.FileInfo] ( $myinvocation.mycommand.path ) ).directory ) `
        -childPath 'ITG.WinAPI\ITG.WinAPI' `
    ) `
;

$ie = New-Object -Comobject InternetExplorer.application;

$get_tokenURI = [System.Uri]"https://pddimp.yandex.ru/get_token.xml?domain_name=$( [System.Uri]::EscapeDataString( $domain ) )";
$get_tokenAuthURI = [System.Uri]"https://passport.yandex.ru/passport?mode=auth&msg=pdd&retpath=$( [System.Uri]::EscapeDataString( $get_tokenURI ) )";
$ie.Navigate( $get_tokenAuthURI );
$ie.Visible = $True;
$res = [ITG.WinAPI]::SetWindowPos($ie.HWND, (-1), 0, 0, 0, 0, 0x0001 -bor 0x0002);
$res = [ITG.WinAPI]::SetForegroundWindow($ie.HWND);

while ( `
    $ie.Busy `
    -or (-not ([System.Uri]$ie.LocationURL).IsBaseOf( $get_tokenURI ) ) `
) { Sleep -milliseconds 100; };

$token = ( $ie.document.documentElement.innerhtml ).ok.token;

$ie.Quit(); 
$res = [System.Runtime.InteropServices.Marshal]::ReleaseComObject( $ie );
Remove-Variable ie;

Да, так всё просто в результате. Если у администратора установлена постоянная авторизация на сервисах Яндекса в IE, тогда от него (администратора) вообще не потребуется дополнительных действий для получения токена, он будет получен полностью автоматически.

Идея крайне проста. Открываем браузер и направляем его на сервис авторизации Яндекса. При этом указываем сервису авторизации, куда следует перейти в случае успешной авторизации – на url API get_token. После этого дожидаемся, пока либо благодаря cookies браузер перейдёт по адресу необходимого нам API, либо пока администратор авторизуется через форму Яндекс.Паспорт и уж затем браузер отправит запрос на указанный нами url API. После чего разбираем содержимое ответа как xml, и получаем токен. Осталось только закрыть браузер, что мы и делаем в самом конце сценария. Безусловно, в приведённом выше примере нет обработки ошибок. Реализуем её чуть позже API Яндекс:Почта для домена и PowerShell: скриптуем get token.

Теперь пришло время оформить приведённый выше набросок сценария в виде модуля и корректно оформленной функции.

Модуль ITG.Yandex.PDD.psm1

Модуль назвал ITG.Yandex.PDD. Итак, сам модуль:

Import-Module `
    (join-path `
        -path $PSScriptRoot `
        -childPath 'ITG.RegExps\ITG.RegExps' `
    ) `
;

Set-Variable `
    -Name DefaultDomain `
    -Value ([string]'') `
;
Set-Variable `
    -Name DefaultToken `
    -Value ([string]'') `
;

function Get-Token {
    <#
        .Component
            API Яндекс.Почты для доменов
        .Synopsis
            Метод (обёртка над Яндекс.API get_token) предназначен для получения авторизационного токена.
        .Description
            Метод get_token предназначен для получения авторизационного токена.
            Авторизационный токен используется для активации API Яндекс.Почты для доменов. Получать токен
            нужно только один раз. Чтобы получить токен, следует иметь подключенный домен, авторизоваться
            его администратором.
            Синтаксис запроса:
                https://pddimp.yandex.ru/get_token.xml ? domain_name =<имя домена>
            Получение токена для домена yourdomain.ru:

https://pddimp.yandex.ru/get_token.xml?domain_name=yourdomain.ru

            Формат ответа
            Если ошибок нет, метод возвращает <ok token="..."/>, в противном случае - <error reason='...'/>.
            Но данная функция возвращает непосредственно токен, либо генерирует исключение.
        .Outputs
            [System.String] - собственно token
        .Link

http://api.yandex.ru/pdd/doc/api-pdd/reference/get-token.xml#get-token

        .Example
            Получение токена для домена yourdomain.ru:
                $token = Get-Token -DomainName 'yourdomain.ru';
    #>

    [CmdletBinding()]
    
    param (
        [Parameter(
            Mandatory=$true,
            Position=0,
            ValueFromPipeline=$true
        )]
        [string]
        [ValidateScript( { $_ -match "^$($reDomain)$" } )]
        [Alias("domain_name")]
        [Alias("Domain")]
        $DomainName = $DefaultDomain
    )

    process {
        Set-Variable `
            -Name DefaultDomain `
            -Scope Script `
            -Value $DomainName `
            -Force `
        ;
        $get_tokenURI = [System.Uri]"https://pddimp.yandex.ru/get_token.xml?domain_name=$( [System.Uri]::EscapeDataString( $DomainName ) )";
        $get_tokenAuthURI = [System.Uri]"https://passport.yandex.ru/passport?mode=auth&msg=pdd&retpath=$( [System.Uri]::EscapeDataString( $get_tokenURI ) )";

        try {
            Write-Verbose 'Создаём экземпляр InternetExplorer.';
            $ie = New-Object -Comobject InternetExplorer.application;
            Write-Verbose "Отправляем InternetExplorer на Яндекс.Паспорт ($get_tokenAuthURI).";
            $ie.Navigate( $get_tokenAuthURI );
            $ie.Visible = $True;
            
            Import-Module `
                (join-path `
                    -path $PSScriptRoot `
                    -childPath 'ITG.WinAPI\ITG.WinAPI' `
                ) `
                -ErrorAction Continue
            ;
            if ( -not $Error ) {
                $res = [ITG.WinAPI]::SetWindowPos($ie.HWND, (-1), 0, 0, 0, 0, 0x0001 -bor 0x0002);
                $res = [ITG.WinAPI]::SetForegroundWindow($ie.HWND);
            };
            
            Write-Verbose 'Ждём либо пока Яндекс.Паспорт сработает по cookies, либо пока администратор авторизуется на Яндекс.Паспорт...';
            while ( `
                $ie.Busy `
                -or (-not ([System.Uri]$ie.LocationURL).IsBaseOf( $get_tokenURI ) ) `
            ) { Sleep -milliseconds 100; };
            $ie.Visible = $False;

            $res = ( $ie.document.documentElement.innerhtml );
            Write-Debug "Ответ API get_token: $($ie.document.documentElement.innerhtml).";
            if ( $res.ok ) {
                $token = $res.ok.token;
                Write-Verbose "Получили токен для домена $($DomainName): $token.";
                Set-Variable `
                    -Name DefaultToken `
                    -Scope Script `
                    -Value $token `
                    -Force `
                ;
                $token;
            } else {
                $errMsg = $res.error.reason;
                Write-Error `
                    -Message "Ответ API get_token для домена $DomainName отрицательный." `
                    -Category PermissionDenied `
                    -CategoryReason $errMsg `
                    -CategoryActivity 'Yandex.API.get_token' `
                    -CategoryTargetName $DomainName `
                    -RecommendedAction 'Проверьте правильность указания домена и Ваши права на домен.' `
                ;
            };
        } finally {
            Write-Verbose 'Уничтожаем экземпляр InternetExplorer.';
            $ie.Quit(); 
            $res = [System.Runtime.InteropServices.Marshal]::ReleaseComObject( $ie );
            Remove-Variable ie;
        };
    }
}  

Export-ModuleMember `
    Get-Token `
;

Теперь уже и ошибки обрабатываю, и параметры контролирую (в частности – проверяю через регулярные выражения, что на самом деле домен передан, а не случайный набор символов). Пример использования этого модуля:

[CmdletBinding()]
param ()

Import-Module `
    (join-path `
        -path ( ( [System.IO.FileInfo] ( $myinvocation.mycommand.path ) ).directory ) `
        -childPath 'ITG.Yandex.PDD' `
    ) `
    -Prefix Yandex `
;

$token = Get-YandexToken 'csm.nov.ru' -ErrorAction Stop;
$token;

Всё просто вот так вот. Исключения решил не генерировать, оставил на усмотрение тех, кто будет использовать командлету (как в примере показано, через –errorAction можно как раз и определить поведение в этом случае, что соответствует стилю PowerShell).

В общем-то, на этом с обёрткой API get_token закончил, немало получилось…

P.S. Модуль ITG.WinAPI.psm1

Как видно в примерах, пришлось воспользоваться WinAPI для некоторых манипуляций с окном браузера (без этих манипуляций окно появлялось под окном сценария, и его можно было банально не заметить, что неудобно явно). Описывать различные методы использования WinAPI в PowerShell не буду, уже масса статей на эту тему, просто приведу код модуля. Сразу прошу – не бейте ногами, он будет работать только на PoSh 2 и старше.

Add-Type @" 

using System; 
using System.Runtime.InteropServices; 

namespace ITG {

public class WinAPI { 
    
    // http://msdn.microsoft.com/en-us/library/windows/desktop/ms633545(v=vs.85).aspx
    [DllImport("user32.dll")] 
    [return: MarshalAs(UnmanagedType.Bool)] 
    public
    static
    extern
    bool
    SetWindowPos(
        IntPtr hWnd, 
        IntPtr hWndInsertAfter,
        int x, int y, 
        int cx, int cy, 
        uint uFlags
    );

    // http://msdn.microsoft.com/en-us/library/windows/desktop/ms633539(v=vs.85).aspx
    [DllImport("user32.dll")] 
    [return: MarshalAs(UnmanagedType.Bool)] 
    public
    static
    extern
    bool
    SetForegroundWindow(
        IntPtr hWnd
    );

} 

}

"@;

Export-ModuleMember `
;

По аналогии можно использовать и другие функции WinAPI, но мне пока что хватило приведённых выше.

P.S. В следующих статьях приведу код функций на PoSh, реализующих обёртку для других методов API Яндекса. Так постепенно и автоматизируем процесс создания ящиков и групп рассылок на Яндекс.Почта для доменов.

Отзывы » (2)

  1. Случайно наткнулся на интересную ссылку http://kbyte.ru/ru/Programming/Articles.aspx?id=42&mode=art. Автор как раз описывает авторизацию на Яндексе скриптом. Но в моём случае авторизация через браузер мне нравится больше.

  2. а вот и библиотека для работы с API Яндекс.Почты для домена на Питоне — https://github.com/V-Alexeev/yandexmailapi/blob/master/YandexMailApi.py

Опубликовать комментарий

XHTML: Вы можете использовать следующие HTML теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Tags Связь с комментариями статьи:
RSS комментарии
Обратная ссылка