PowerShell ile Twitter REST API Entegrasyonu

26.11.2015
1

Twitter servislerine genelde web tarayıcılar ve mobil uygulamalar üzerinden erişilir. Aynı zamanda bu servisler bazı işlerin programatik olarak gerçekleştirilebilmesi için REST, Streaming, Ads gibi çeşitli API’lar da sunar. Örneğin yeni bir tweet yazmak, timeline okumak, takipçi listesi almak, görsel yüklemek, arama yapmak, ayarları değiştirmek, direkt mesaj göndermek gibi birçok aktiviteyi bu API’lar sayesinde kendi geliştirdiğiniz uygulamalara entegre edebilir ve hatta tamamen özelleştirilmiş bir Twitter client bile yazabilirsiniz. Ben de geçenlerde HTTP tabanlı API istekleri, JSON formatlı veri alışverişi ve bu sırada oluşan içeriklerin parse edilmesi gibi birkaç konu üzerinde çalışıyordum ki aklıma Twitter REST API’lar geldi. Bu API’lar hem istediğim deneme ortamını sağlıyor hem de yetkilendirme için OAuth kullanıyordu; bu da anlamak istediğim bir başka konuydu…

Eğer Twitter kullanıyorsanız mesela “tweet atın”, “takipçilerinizi analiz edin” veya “unfollow yapanları öğrenin” gibi çeşitli üçüncü parti servisleri görmüşsünüzdür. Tamamı yine bu gibi API’lar vasıtasıyla çalışırlar. Ancak bana göre bu tip servislerdeki en büyük sorun kullanıcının username/password bilgisiyle çalışıyor olmaları. Aslında güvensiz sayılmaz çünkü bu sırada yine Twitter tarafından sağlanan bir yöntem kullanılır ama sanırım bugüne kadar kullandığım hiçbir servisin username/password bilgisini -o servisle ilişkili olsa dahi- farklı bir servise/uygulamaya girmedim. Eğer seçeneğim yoksa da o servisi kullanmadım. Şimdi oturup bir Twitter client yazmayacağım tabii ki ama bu gibi meselelerin üzerinde düşünmeyi severim. Eğer bir programlama dilini konuşuyor olsaydık (ki çoğu zaman hazır kütüphanelere sahiptirler) bu gibi entegrasyonlar yapmak daha kolay olabilirdi. Peki ya PowerShell ile böyle bir entegrasyon mümkün mü? Mesela PowerShell ile yazılmış bir Twitter client? Pek tabii mümkün :) Ancak UX açısından çok kullanışlı olacağını söyleyemem. Ama yine de mümkün. Geçenlerde PowerShell üzerinden tweet atmak, tweet okumak, direct message göndermek, follower’ları listelemek gibi şeyler yapan birkaç script hazırlamıştım.

powershell-tweet

Aralarından bir tanesini fikir vermesi ve yol göstermesi adına bu blog post’da kaleme almak istedim. Bu PoC sırasında -ve tabii herhangi bir üçüncü taraf servise username/password bilgisi girmeden- ulaşmak istediğim hedef şu: Twitter hesabım için Takipçiler kim? Kim yeni takibe başladı? Kim ne zaman takip etmeyi bıraktı? gibi bilgilere sahip olmak.

Bu bilgileri sağlayan Get-Followers isimli PowerShell Script iki parametreye sahip. Parametrelerden birisi sizi takip eden tüm follower’ları detay bilgileriyle birlikte CSV formatlı bir dosyaya export ediyor. Diğer parametre ise export edilmiş iki CSV dosyasını karşılaştırarak yeni follower’ları veya sizi unfollow edenleri tespit ediyor. Export edilen follower list’de yer alan bilgiler şunlar:

  • İsim
  • Kullanıcı adı
  • ID
  • Lokasyon bilgisi
  • Takip ettiği kişi sayısı
  • Onu takip eden kişi sayısı
  • Bu güne kadar attığı tweet sayısı
  • Like sayısı
  • Hesabını hangi tarihte açtığı
  • En son tweet attığı tarih
  • Profilin korumalı (protected) olup olmadığı
  • Muting ve Blocking durum bilgisi
  • NewFollower/Follower/UnFollower durum bilgisi

Get-Followers Neyi Nasıl Yapar?

  • GET followers/list API‘ını kullanır.
  • Parolanızı istemez. Onun yerine Twitter App bilgilerini kullanır.
  • Yüksek takipçi sayısına sahip kişilerin API Rate limit’lere takılmaması için birden fazla Twitter App key ve token bilgisi girmeyi destekler.
  • -Export parametresi ile takipçi listesini CSV dosyasına export eder.
  • -Compare parametresi ile geçmişte export edilmiş iki takipçi listesini karşılaştırarak yeni follower’ları ve unfollow edenleri tespit eder.

PowerShell ve Twitter REST API ile Takipçi Listesi Export (CSV)

Bu iş için gerçekleştirmeniz gereken adımlar kısaca aşağıdaki gibi.

  1. Hesabınızla ilişkili bir (veya daha fazla sayıda) Twitter App oluşturun.
  2. Twitter App için izinleri belirleyin, Key ve Token bilgilerini not alın.
  3. PowerShell script’i hazırlayın.
  4. Çalıştırın.

1) Twitter App Oluşturun

Twitter’ın Application Management konsolunda oturum açın.

twitter-app-olusturma-adim1

Create New App ile bir veya daha fazla sayıda Twitter App oluşturun. Bu app’ler HTTP tabanlı REST API’lara request gönderirken yetkilendirme (OAuth/authorization) sırasında kullanılacak. Bu sayede hesabınıza tam erişim sağlayan username/password bilgisi girmek zorunda kalmamış olacaksınız.

Buraya dikkat: Her bir Twitter REST API’a özel farklı request limitleri vardır ve bu limitler App başına kontrol edilir. Örneğin GET followers/list API’ına aynı App ile 15dk. içerisinde maksimum 15 adet request yapılabilir (user auth). Bu sırada her bir request içerisinde maksimum 200 adete kadar follower bilgisi alınabilir. Bunlar tasarımsal limitler. Bu durumda 15 request ile en fazla 3.000 follower’a sahip bir hesabı listeleyebilirsiniz. Ama mesela 9.000 takipçiniz varsa? O zaman birden fazla (örneğin 3 farklı) Twitter App oluşturmanız ve request’leri yapan fonksiyonu bir App’in limiti dolduğunda diğer App’ten devam edecek şekilde kodlamanız gerekir. Bir nevi şarjör değiştirmek, jeton atmak… Yeni bir Twitter App oluşturmak için gerekli bilgileri doldurun. Agreement’ı kabul edin ve sayfanın altındaki Create your Twitter application butonu ile oluşturma işlemini tamamlayın. Name ve Website bilgilerinin pek bir önemi yok.

twitter-app-olusturma-adim2

Sonrasında aşağıdaki gibi görünüyor olmalı.

twitter-app-olusturma-adim3

2) Twiter App İzinlerini Ayarların, Key ve Token Bilgilerini Alın

Hesabınızla ilişkili olarak oluşturduğunuz Twitter App’i herhangi bir API entegrasyonunda kullanabilmeniz için aşağıdaki 4 bilgiye ihtiyacınız var.

  • Consumer Key (API Key)
  • Consumer Secret (API Secret)
  • Access Token
  • Access Token Secret

ⓘ  Bu bilgilerin gizliliğini sağlamak oldukça önemlidir.

Öncelikle ilgili Twitter App için Permissions tabından gerekli izinleri atayın. Mesela birazdan Twitter’ın GET followers/list API’ını kullanarak takipçi listesi alacağım. Yani sadece GET yapılacak. Bu yüzden Twitter App’e yalnızca Read only vermek gerekli olan minimum yetkilendirmeyi sağlayacağı gibi güvenlik açısından da iyi bir fikirdir. Sayfanın altındaki Update Settings butonu ile onaylayın.

twitter-app-keys-adim1

Ardından Keys and Access Tokens tabına gidin. Application Settings bölümünden Consumer Key ve Consumer Secret bilgilerini alın.

twitter-app-keys-adim2

Yine aynı tab’da Your Access Token bölümünde Create my access token butonu ile token bilgilerini üretin ve alın.

twitter-app-keys-adim3

Ben Script kodlarında temsili olarak aşağıdaki gibi maskeleyeceğim. Siz bunları kendi App bilgilerinizle değiştirmeyi unutmayın.

  • Consumer Key: cKeyXXXXXXXXXX1
  • Consumer Secret: cSecretXXXXXXXXXX1
  • Access Token: aTokenXXXXXXXXXX1
  • Access Secret: aSecretXXXXXXXXXX1

ⓘ  Anahtarları üretme ve alma işlemini Permissions tabındaki yetkilendirmeden sonra yapmalısınız. Eğer ileride bu App üzerinde bir yetki değişikliği yaparsanız, Access Token’ları yeniden üretmeniz ve oluşan yeni bilgileri kullanmanız gerekir.

3) PowerShell Script’i Hazırlayın

Parça parça ele alacağım. Eğer sormak istedikleriniz olursa mesaj atabilir veya yazı altına yorum bırakabilirsiniz.

Script parametreleri:

#
# Serhat AKINCI - @serhatakinci
# This script exports your Twitter followers and their details to CSV file.
# It also compares the two CSV files to see unfollowers when needed.
#

# Script Parameters
Param (
[CmdletBinding(SupportsShouldProcess=$True)]
[parameter(ParameterSetName="Set1",Position=0,Mandatory=$true,HelpMessage='Writes the follower list to csv file.')]
[switch]$Export = $false,
[parameter(ParameterSetName="Set2",Position=0,Mandatory=$true,HelpMessage='Compares the two CSV files to see changes like new followers or unfollowers.')]
[switch]$Compare = $false,
[parameter(ParameterSetName="Set2",Position=0,Mandatory=$true,HelpMessage='Reference CSV file for comparison. (in other words, the older csv file)')]
$ReferenceObject,
[parameter(ParameterSetName="Set2",Position=0,Mandatory=$true,HelpMessage='Difference CSV file for comparison. (in other words, the newer csv file)')]
$DifferenceObject
)

-Export parametresi, follower list’i alır ve script’in çalışma dizinine bir CSV dosyası olarak kayıt eder.

-Compare parametresi, -ReferenceObject ve -DifferenceObject parametreleri ile birlikte kullanılır ve daha önce oluşturulmuş iki CSV dosyası arasındaki farkları bulur.

Key ve Token array:

# Key and Token Array
# Format: ItemNumber = "Consumer Key","Consumer Secret","Access Token","Access Token Secret"
$tokenArray = @{
1 = "cKeyXXXXXXXXXX1","cSecretXXXXXXXXXX1","aTokenXXXXXXXXXX1","aSecretXXXXXXXXXX1"
2 = "cKeyXXXXXXXXXX2","cSecretXXXXXXXXXX2","aTokenXXXXXXXXXX2","aSecretXXXXXXXXXX2"
# 3 = "Consumer Key","Consumer Secret","Access Token","Access Token Secret"
# 4 = "Consumer Key","Consumer Secret","Access Token","Access Token Secret"
# ...
}

Hani şu şarjör meselesi… Eğer follower sayınız 3.000’in üzerindeyse veya 15dk. limitine takılmadan daha fazla request gerçekleştirmek istiyorsanız şarjörü farklı Twitter App bilgileriyle doldurmanız gerekir. Bu array’deki item sıralaması önemli. Mutlaka şu şekilde olmalı: <Consumer Key>,<Consumer Secret>,<Access Token>,<Access Token Secret>. Yukarıda bir örneği var.

Bir takım başlangıç değeri atamaları:

# Initial values of key/token
$oauth_Consumer_Key = $tokenArray.Item(1)[0]
$oauth_Consumer_Secret = $tokenArray.Item(1)[1]
$oauth_Token = $tokenArray.Item(1)[2]
$oauth_Token_Secret = $tokenArray.Item(1)[3]

# Other Variables
$resourceURL = "https://api.twitter.com/1.1/followers/list.json"
[Int64]$Cursor = -1
$fCount = 0
$outputArray1 = @()
$Resume = 1
$Result = 1
$itemNumber = 1

$oauth_ ile başlayanlar şarjördeki ilk App bilgilerini temsil eder. Değiştirmeden kullanın.

$resourceURL follower’ları listeleyen JSON tabanlı Twitter API’ına ait sabit URL’i temsil eder. Bu API için değiştirmeden kullanın.

$Cursor API’den dönen içeriğin sayfalara bölünmesi durumunda kullanılır. Twitter buna Cursoring diyor. Mesela follower list için tek sayfada maksimum 200 kullanıcı bilgisi yer alabilir demiştim ya, işte sonraki kullanıcılar sayfalara bölünerek ve ancak ikinci, üçüncü sorgularda listelenebilir. Bunu API request içerisinde hangi sayfaya bakmak istediğimizi belirten bir değişken olarak düşünebilirsiniz. Eğer herhangi bir atama yapılmazsa varsayılan değeri -1‘dir ve bu ilk sonuç sayfasını temsil eder. Değiştirmeden kullanın.

$fCount Takipçi sayısını tutar.

$outputArray1 Takipçi bilgilerini tutan ve döngüler içerisinde eklene eklene büyüyen bir array’dir. CSV’ye export edilecek liste bu değişkende birikir.

Gelelim en önemli fonksiyona…

Şu anki haliyle sadece Get followers/list adına optimize edilmiş ve OAuth tabanlı authorization koşullarını sağlayan, ancak gerektiğinde ufak değişikliklerle Twitter’ın tüm REST API’larına istek gönderirken kullanabileceğiniz bir fonkisyon yazdım. Temelde yaptığı iş mekanik kod bölümünde -yani birazdan- Invoke edilecek URI için gerekli Authorization String’i üretmektir. Bu fonksiyonun implementasyon detaylarını merak ediyorsanız: request overview, encoding, signature gibi bağlantılara bakabilirsiniz.

New-OAuthAuthorization fonksiyonu:

# Functions
function New-OAuthAuthorization # for GET followers/list API
{
# Parameters
param (
[Parameter(Mandatory)]
[string]$ResourceURL,
[Parameter(Mandatory)]
[string]$Method,
[Parameter(Mandatory)]
[Int64]$NextCursor,
[Parameter(Mandatory)]
$ConsumerKey,
[Parameter(Mandatory)]
$ConsumerSecret,
[Parameter(Mandatory)]
$AccessToken,
[Parameter(Mandatory)]
$AccessTokenSecret
)

# Unique token for each unique request (Nonce)
$oauth_Nonce = [System.Convert]::ToBase64String(([System.Text.Encoding]::ASCII.GetBytes("$([System.DateTime]::Now.Ticks.ToString())00000"))).Replace('=', 's')

# The oauth_timestamp parameter indicates when the request was created.
$timeSpan = (New-TimeSpan -Start ([System.DateTime]::ParseExact("01/01/1970", "dd/MM/yyyy", $null)) -End ([System.DateTime]::UtcNow)).TotalSeconds
$oauth_Timestamp = [System.Convert]::ToInt64($timeSpan).ToString()

# The signature parameter contains a value which is generated by running all of the other request parameters and two secret values through a signing algorithm.
$signData = "$([System.Uri]::EscapeDataString($resourceURL))&"
$signParams = @{
oauth_consumer_key = $consumerKey
oauth_nonce = $oauth_Nonce
oauth_signature_method = 'HMAC-SHA1'
oauth_timestamp = $oauth_Timestamp
oauth_token = $accessToken
oauth_version = '1.0'
count = '200'
cursor = $nextCursor
}
$signParams.GetEnumerator() | Sort-Object Name | foreach{$signData += [System.Uri]::EscapeDataString("$($_.Key)=$($_.Value)&".Replace(',','%2C').Replace('!','%21'))}
$signData = "GET&" + $signData
$signData = $signData.TrimEnd('%26')
$signKey = [System.Uri]::EscapeDataString($consumerSecret) + "&" + [System.Uri]::EscapeDataString($accessTokenSecret)
$hmacSHA1 = New-Object System.Security.Cryptography.HMACSHA1
$hmacSHA1.Key = [System.Text.Encoding]::ASCII.GetBytes($signKey)
$oauth_Signature = [System.Convert]::ToBase64String($hmacSHA1.ComputeHash([System.Text.Encoding]::ASCII.GetBytes($signData)));

## Building the HTTP autorization header string
$authParams = $signParams
$authParams.oauth_signature = $oauth_Signature
$authParams.Remove('cursor')
$authParams.Remove('count')
$authString = 'OAuth '
$authParams.GetEnumerator() | Sort-Object Name | foreach{$authString += $_.Key + '="' + [System.Uri]::EscapeDataString($_.Value) + '", '}
$authString = $authString.TrimEnd(', ')

$authString
}

Script’in mekanik kodu bu bölüm:

# Process
Write-Host "INFO  - Script started!" -ForegroundColor Cyan
if($Export)
{
# CSV Name
$csvPrefix = "Followers"

Do
{
$authorizationString = New-OAuthAuthorization -ResourceURL $resourceURL -Method GET -NextCursor $Cursor -ConsumerKey $oauth_Consumer_Key -ConsumerSecret $oauth_Consumer_Secret -AccessToken $oauth_Token -AccessTokenSecret $oauth_Token_Secret
$Data = $null
Try
{
$Data = Invoke-RestMethod -Uri "$($resourceURL)?count=200&cursor=$($Cursor)" -Method GET -Headers @{'Authorization' = $authorizationString} -ContentType 'application/x-www-form-urlencoded' -ErrorVariable err1
$Resume = 1
}
Catch
{
if($err1 -like "*`"code`":88*")
{
if($itemNumber -lt $tokenArray.count)
{
$itemNumber = $itemNumber + 1
$oauth_Consumer_Key = $tokenArray.Item($itemNumber)[0]
$oauth_Consumer_Secret = $tokenArray.Item($itemNumber)[1]
$oauth_Token = $tokenArray.Item($itemNumber)[2]
$oauth_Token_Secret = $tokenArray.Item($itemNumber)[3]
$Resume = 0
$Result = 0
}
else
{
Write-Host "ERROR - API rate limit exceeded." -ForegroundColor Red
Write-Host "ERROR - Please add new Access Tokens in the `$tokenArray or try again 15min later." -ForegroundColor Red
$Result = 0
Break
}
}
else
{
$Result = 0
Break
}
}

if($Resume -eq 1)
{
foreach($i in $Data.users)
{
$charger1 = @{
Name = $i.name
Username = $i.screen_name
UserID = $i.id
Location = $i.location
Tweets = $i.statuses_count
Followers = $i.followers_count
Following = $i.friends_count
Favorites = $i.favourites_count
Joined = $i.created_at
LastTweet = $i.status.created_at
Protected = $i.protected
Muting = $i.muting
Blocking = $i.blocking
Status = "Follower"
}

$date1 = $($i.created_at).Split()
$charger1.Joined = ($date1[2] + " " + $date1[1] + " " + $date1[5] + " " + $date1[3])

if($i.status.created_at)
{
$date2 = ($i.status.created_at).Split()
$charger1.LastTweet = ($date2[2] + " " + $date2[1] + " " + $date2[5] + " " + $date2[3])
}
elseif($i.statuses_count -eq 0){$charger1.LastTweet = "No any tweets"}
elseif($i.protected -eq $true){$charger1.LastTweet = "Tweets are protected"}
else{$charger1.LastTweet = "Unknown"}

$customObj1 = New-Object PSObject -Property $charger1
$outputArray1 += $customObj1
$fCount = $fCount + 1
}
$Cursor = $Data.next_cursor
$Result = 1
}
}
Until ($Data.next_cursor -eq 0)

}
elseif($Compare)
{
# CSV Name
$csvPrefix = "Difference"

$refObj = Import-Csv $ReferenceObject -Delimiter ";"
$difObj = Import-Csv $DifferenceObject -Delimiter ";"
$Difference = Compare-Object -ReferenceObject $refObj -DifferenceObject $difObj -Property UserID
$Result = 1

if($Difference)
{
foreach($item in $Difference)
{
if($item.SideIndicator -eq "<=")
{
Write-Host "debug1"
$i = $refObj | ?{$_.UserID -eq $item.UserID}
$i.Status = "Unfollower"
$outputArray1 += $i
$fCount = $fCount + 1
}
if($item.SideIndicator -eq "=>")
{
Write-Host "debug2"
$i = $difObj | ?{$_.UserID -eq $item.UserID}
$i.Status = "New Follower"
$outputArray1 += $i
$fCount = $fCount + 1
}
}
}
else
{
$charger1 = @{
Name = "No difference"
}
$customObj1 = New-Object PSObject -Property $charger1
$outputArray1 += $customObj1
}
}
else
{
Write-Host "ERROR - Unknown Error." -ForegroundColor Red
$Result = 0
Break
}

Ve son olarak tüm sonuçları bir CSV’ye yazıyorum:

# Out to CSV
if($Result -eq 1)
{
$outputArray1 | select Name,Username, UserID, Location, Followers, Following, Tweets, Favorites, `
Joined, LastTweet, Protected, Muting, Blocking, Status | `
Export-Csv $($csvPrefix + "_" + $fCount + "_" + (Get-Date -UFormat %d-%b-%y_%H-%M-%S.csv)) -NoTypeInformation -Delimiter ";" -Encoding UTF8

Write-Host "INFO  - Completed successfully!" -ForegroundColor Cyan
}
else
{
Write-Host "ERROR - $($Error[0].ErrorDetails.Message)" -ForegroundColor Red
}

İndirme bağlantısı: Get-Followers

4)  Çalıştırın

İçerisindeki Key & Token array’i düzenledikten sonra follower list export işlemi için .\Get-Followers.ps1 -Export

get-followers-export

Eğer her şey yolunda ise çalışma dizininde Followers_Count_Date_Time isim formatında ve CSV uzantılı bir dosya oluşur. Excel’de açıp ufak bir biçimlendirme sonrasında sonuçları inceleyebilirsiniz.

get-followers

Karşılaştırma işlemi için .\Get-Followers.ps1 -Compare -ReferenceObject 1.csv -DifferenceObject 2.csv

get-followers-compare

Test:
-ReferenceObject daha önce export edilmiş CSV’lerden eski tarihli olanı, -DifferenceObject ise daha yeni tarihli olanı temsil eder. Aradaki fark yeni takipçileri veya takip etmeyi bırakanları ortaya çıkartır. Eğer her şey yolunda ise çalışma dizininde Difference_Count_Date_Time isim formatında ve CSV uzantılı bir dosya oluşur. Yine Excel’de açıp ufak bir biçimlendirme sonrasında sonuçları inceleyebilirsiniz.

Yorumlar (1)

  1. mehmet

    Merhaba gerçekten güzel bir uygulama olmuş elinize sağlık.
    Çalıştırmadan önce Powershellde “set-executionpolicy remotesigned” komutununu girmek gerekiyor yoksa hata veriyor.
    Birde tweete ait RT kullanıcı listesi alan örnek olsa da güzel olurdu.

Yorum Ekle

* Gerekli

* Gerekli

* Tercihen