Samstag, 22. August 2015

Posts auf Blogger.com veröffentlichen

Im Januar schrieb ich einen Post über das Thema, wie man Posts automatisiert auf Blogger.com veröffentlicht. Dafür wurde jedoch die API v2.0 verwendet, welche nun veraltet ist, daher möchte ich hier zeigen wie man dies mit der API v3.0 umsetzt.
Leider habe ich noch keinen Weg (und ich weiß nicht ob es einen gibt) gefunden wie man Posts komplett ohne Benutzer Interaktion veröffentlicht, in dieser Version muss er sich anmelden und einen Code kopieren.
Zusammengefasst sieht der Ablauf in dieser Version wie folgt aus:
Über die Google Developer Console muss ein neues Projekt erstellt werden und die Blogger API aktiviert werden. Informationen über diesen Schritt und generelle Informationen über die API können hier gefunden werden. Dann, um die API nutzen zu können, müssen wir uns mittels OAuth 2.0 authentifizieren. Dafür müssen wir eine Anfrage an Google senden. Wir tuen dies in dem wir eine bestimmte URL im Browser aufrufen, als Parameter übergeben wir unter anderem die ID unseres Projekts, den sogenannten Scope (Gültigkeitsbereich) etc. Der User loggt sich dann im Browser ein und erhält einen Autorisierungs Code. Diesen Code sendet das Programm dann mittels einem HTTP Request an Google und erhält ein Token zurück. Damit können wir schließlich die API aufrufen und Posts auf Blogger posten. Dieser Teil ist hier beschrieben.

Nach diesem groben Überblick jetzt zur konkreten Implementierung: Zuerst erstellen wir ein neues Projekt in der Google Developer Console. Dann suchen wir die API Blogger API v3 im Menu APIs und aktivieren diese.
In unserem C# Programm müssen wir nun zuerst eine URL für den initialien Request aufrufen. Die benötigte URL ist https://accounts.google.com/o/oauth2/auth. Ein Parameter den wir übergeben ist scope, dieser beschreibt für welche Anwendung wir uns authentifizieren wollen. Wir wählen dafür https://www.googleapis.com/auth/blogger. Der nächste Parameter ist die Redirect URL (redirect_uri), welche beschreibt wohin die Antwort gesendet wird. Wird diese auf urn:ietf:wg:oauth:2.0:oob gesetzt, wird die Antwort in dem geöffneten Browser angezeigt.
Mittels response_type=code stellen wir ein einen Code zurück zu kriegen. Als letzten Parameter setzen wir client_id auf die ID unseres erstellten Projekts in der Google Developer Console. Dies ist NICHT die Projekt ID welche auf der Hauptseite angezeigt wird, sondern um die Client ID auszulesen muss man unter APIs & auth - Credentials auf Add Credentials - OAuth 2.0 client ID klicken (falls nicht bereits getan). Dann wählen wir Other aus (da wir eine native Applikation entwickeln) und klicken auf Create - dann erhalten die wir Client ID.
Insgesamt sollte die aufgerufene URL so aussehen:

https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/blogger&redirect_uri=urn:ietf:wg:oauth:2.0:oob&response_type=code&client_id=client-id


Wir benutzen Process.Start mit der URL um den Standardbrowser mit dieser zu öffnen. In diesem wird dem Benutzer dann ein Login und Zustimmungsbildschirm präsentiert. Falls er auf akzeptieren klickt, wird ein Erfolgs Code präsentiert. Wir kopieren diesen.
Mit diesem Code können wir ein Access Token für die Nutzung der Blogger API erhalten. Dafür müssen wir einen HTTP Post Request durchführen. Die URL die wir aufrufen müssen ist https://www.googleapis.com/oauth2/v3/token, als Parameter übergeben wir den erhaltenen Code (code), die Client ID (client_id), die Redirect URL (redirect_uri, die gleiche wie vorher), den Grant Type (grant_type=authorization_code - beschreibt wie wir uns authentifizieren wollen) und das Client Secret (client_secret). Das letztere ist sichtbar wenn man auf Client ID unter Credentials klickt.
Mit der Funktion HTTPPost(), welche im verlinkten Post beschrieben ist, sieht das wie folgt aus:
            string Code = "code=" + Code1 + "&";
            string ID = "client_id=id&";
            string uri = "redirect_uri=urn:ietf:wg:oauth:2.0:oob&";
            string grant = "grant_type=authorization_code&";
            string secret = "client_secret=secret";

            string Code2 = HTTPPost("https://www.googleapis.com/oauth2/v3/token", Code + ID + uri + grant + secret);

Wir lesen die Antwort des Servers aus da diese (im Erfolgsfall) das Access Token enthält. Die Antwort wird im JSON Format zurückgegeben, wir benutzten die Library Newtonsoft.Json um diese zu interpretieren. Vielleicht schreibe ich demnächst einen Post über die Library, fürs Erste verweise ich hier nur auf diesen Post, wo sie auch benutzt wird.
Also erhalten wir das Access Token mit dem folgenden Code, wobei AccessToken eine selber erstellte Klasse mit den gewünschten Attributen ist:

AccessToken JsonAccToken = (AccessToken)JsonConvert.DeserializeObject(Code2, typeof(AccessToken));
string StrAccToken = JsonAccToken.access_token;

Mit diesem Token können wir nun die API benutzen um Posts auf Blogger zu veröffentlichen. Wir benutzen einen WebRequest um den entsprechenden POST Request an den Blogger Server zu senden. Als Ziel Adresse setzen wir https://www.googleapis.com/blogger/v3/blogs/.
Als nächsten setzen wir dann den entsprechenden Content Type, den Authentifizierungs Header etc:

var http = (HttpWebRequest)WebRequest.Create(new Uri("https://www.googleapis.com/blogger/v3/blogs/" + sid + "/posts/"));
http.Accept = "application/json";
http.ContentType = "application/json";
http.Method = "POST";
http.Headers.Add("Authorization""Bearer " + token);

sid ist die ID des Blogs auf welchem wir veröffentlichen wollen. Als nächstes beschreiben wir den Post im JSON Format, dafür schreiben wir ihn zuerst als String und konvertieren ihn dann.

var vm = new { kind = "blogger#post", blog = new { id = sid }, title = stitle, content = scontent };
var dataString = JsonConvert.SerializeObject(vm);
string parsedContent = dataString;

Im Code bezeichnet stitle den Titel des Posts, scontent den Inhalt (welcher im HTML Format erwartet wird).
Schließlich laden wir dies mit dem WebRequest hoch.
Der komplette Code sieht so aus:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using System.Net;
using System.IO;
using Newtonsoft.Json;

namespace Blogger
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            System.Diagnostics.Process.Start("https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/blogger&redirect_uri=urn:ietf:wg:oauth:2.0:oob&response_type=code&client_id=client-id");
        }

        private string HTTPPost(string url, string postparams)
        {
            string responseString = "";

            // performs the desired http post request for the url and parameters
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            // request.CookieContainer = Cookie; // explicitely use the cookiecontainer to save the session

            string postData = postparams;
            byte[] data = Encoding.UTF8.GetBytes(postData);

            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";
            request.ContentLength = data.Length;

            using (var stream = request.GetRequestStream())
            {
                stream.Write(data, 0, data.Length);
            }

            var response = (HttpWebResponse)request.GetResponse();

            responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();

            return responseString;

        }

        private void Form1_Click(object sender, EventArgs e)
        {
            string Code = "code=" + textBox1.Text + "&";
            string ID = "client_id=client-id&";
            string uri = "redirect_uri=urn:ietf:wg:oauth:2.0:oob&";
            string grant = "grant_type=authorization_code&";
            string secret = "client_secret=secret";

            string Code2 = HTTPPost("https://www.googleapis.com/oauth2/v3/token", Code + ID + uri + grant + secret);

            AccessToken JsonAccToken = (AccessToken)JsonConvert.DeserializeObject(Code2, typeof(AccessToken));
            string StrAccToken = JsonAccToken.access_token;

            JSONPublish(BlogID, "Testpost""This is a Test.", StrAccToken);
        }

        private void JSONPublish(string sid, string stitle, string scontent, string token)
        {
            var http = (HttpWebRequest)WebRequest.Create(new Uri("https://www.googleapis.com/blogger/v3/blogs/" + sid + "/posts/"));
            http.Accept = "application/json";
            http.ContentType = "application/json";
            http.Method = "POST";
            http.Headers.Add("Authorization""Bearer " + token);

            var vm = new { kind = "blogger#post", blog = new { id = sid }, title = stitle, content = scontent };
            var dataString = JsonConvert.SerializeObject(vm);
            string parsedContent = dataString;

            Byte[] bytes = Encoding.UTF8.GetBytes(parsedContent);

            Stream newStream = http.GetRequestStream();
            newStream.Write(bytes, 0, bytes.Length);
            newStream.Close();

            var response = http.GetResponse();

            var stream = response.GetResponseStream();
            var sr = new StreamReader(stream);
            var content = sr.ReadToEnd();
        }

        public class AccessToken
        {
            [JsonProperty(PropertyName = "access_token")]
            public string access_token { get; set; }
        }
    }
}