Skip to content

Instantly share code, notes, and snippets.

@siketyan
Created April 6, 2019 14:03
Show Gist options
  • Save siketyan/e16d927943ac27ce763f8269c4831582 to your computer and use it in GitHub Desktop.
Save siketyan/e16d927943ac27ce763f8269c4831582 to your computer and use it in GitHub Desktop.
Simple Twitter Media Grabbing App w/ 30-day Search API
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using CoreTweet;
using Newtonsoft.Json;
namespace TwiGrab
{
public class App
{
private const string ConsumerKey = "";
private const string ConsumerSecret = "";
private const string AccessToken = "";
private const string AccessTokenSecret = "";
private const string LogFile = "twigrab.log";
private const string OutputDirectory = "out";
private string _keyword;
private FileStream _stream;
private StreamWriter _writer;
private WebClient _client;
public App(string[] args)
{
_keyword = args[0];
_stream = new FileStream("twigrab.log", FileMode.OpenOrCreate, FileAccess.Write);
_writer = new StreamWriter(_stream);
_client = new WebClient();
_writer.AutoFlush = true;
}
~App()
{
_writer.Flush();
_writer.Close();
_stream.Close();
}
public static void Main(string[] args) => new App(args).MainAsync().Wait();
public async Task MainAsync()
{
var tokens = Tokens.Create(
ConsumerKey,
ConsumerSecret,
AccessToken,
AccessTokenSecret
);
Directory.CreateDirectory(OutputDirectory);
string next = null;
var db = new List<Status>();
try
{
while (true)
{
await LogLineAsync("-> Fetching Tweets");
var tweets = await tokens.Tweets.SearchAsync(
"30day",
"dev",
$"{_keyword} has:media",
maxResults: 100,
next: next
);
if (tweets.Count == 0)
{
await LogLineAsync(" -> Found 0 tweets, so all tweets grabbed!");
break;
}
await LogLineAsync($" -> Found {tweets.Count} tweets");
foreach (var tweet in tweets)
{
await LogLineAsync($" -> Reading Tweet #{tweet.Id}");
db.Add(tweet);
await DownloadMedia(tweet.ExtendedEntities?.Media);
}
next = tweets.Next;
}
await LogLineAsync("-> Saving to database");
var json = JsonConvert.SerializeObject(db);
await File.WriteAllTextAsync("db.json", json);
}
catch(Exception e)
{
await ErrorAsync($"-> Error: {e.Message}");
await ErrorAsync(e.StackTrace);
}
}
private async Task DownloadMedia(IEnumerable<MediaEntity> media)
{
if (media is null)
{
return;
}
foreach (var m in media)
{
try
{
if (m.Type == "photo")
{
var url = m.MediaUrlHttps;
var filename = Path.GetFileName(url);
await LogAsync($" -> Downloading image: {filename} ... ");
var isSkipped = await DownloadAsync(
$"{url}:orig",
Path.Combine(OutputDirectory, filename)
);
await LogLineAsync(isSkipped ? "Skipped" : "OK");
}
else if (m.Type == "video")
{
var variant = m.VideoInfo.Variants.OrderByDescending(v => v.Bitrate).First();
var url = variant.Url;
var filename = Path.GetFileName(url.Split('?').First());
await LogAsync($" -> Downloading video: {filename} ... ");
var isSkipped = await DownloadAsync(
$"{url}",
Path.Combine(OutputDirectory, filename)
);
await LogLineAsync(isSkipped ? "Skipped" : "OK");
}
else
{
await WarnAsync(" -> Unknown media type, skipping");
}
}
catch (Exception e)
{
await ErrorAsync($"\n -> Error: {e.Message}");
await ErrorAsync(e.StackTrace);
}
}
}
private async Task<bool> DownloadAsync(string url, string path)
{
if (File.Exists(path))
{
return true;
}
await _client.DownloadFileTaskAsync(url, path);
return false;
}
private async Task LogAsync(string message)
{
Console.Write(message);
await _writer.WriteAsync(message);
}
private async Task LogLineAsync(string message)
{
Console.WriteLine(message);
await _writer.WriteLineAsync(message);
}
private async Task WarnAsync(string message)
{
Console.ForegroundColor = ConsoleColor.Yellow;
await LogLineAsync(message);
Console.ResetColor();
}
private async Task ErrorAsync(string message)
{
Console.ForegroundColor = ConsoleColor.Red;
await LogLineAsync(message);
Console.ResetColor();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment