Closed wilzbach closed 6 years ago
Here's a TwitterSink
- tweet
is already working.
I didn't know how to get to the title, user and category of a post.
Plus I used std.net.curl as it was easier to debug.
module ircsink;
import std.datetime;
import std.string;
import common;
import user;
import ae.net.oauth.common;
final class TwitterSink : NewsSink
{
static struct Config
{
string consumerKey;
string consumerKeySecret;
string oauthAccessToken;
string oauthAccessTokenSecret;
}
this(Config config)
{
this.config = config;
OAuthConfig oAuthConfig = {
consumerKey: config.consumerKey,
consumerSecret: config.consumerKeySecret
};
OAuthSession session = {
config: oAuthConfig,
token: config.oauthAccessToken,
tokenSecret: config.oauthAccessTokenSecret
};
this.session = session;
}
protected:
override void handlePost(Post post, Fresh fresh)
{
import std.uri;
if (!fresh)
return;
if (post.time < Clock.currTime() - dur!"days"(1))
return; // ignore posts older than a day old (e.g. StackOverflow question activity bumps the questions)
// TODO: if NG.announce
// TODO: get title + user
tweet("%s by %s #dlang".format("title", "author"));
}
void tweet(string message)
{
import std.algorithm, std.array;
import std.net.curl;
import std.format;
import std.datetime;
import std.stdio;
import ae.net.http.common;
import ae.net.ietf.url;
auto parameters = new UrlParameters[](1);
parameters[0]["status"] = message;
string url = "https://api.twitter.com/1.1/statuses/update.json";
string queryString = parameters.map!(ps => ps.pairs.map!(p => session.encode(p.key) ~ "=" ~ session.encode(p.value))).joiner.join("&");
auto http = HTTP((url ~ "?" ~ queryString));
http.method = HTTP.Method.post;
http.addRequestHeader("Authorization", session.prepareRequest(url, "POST", parameters).oauthHeader);
http.verbose = true;
http.contentLength = 0;
http.perform();
}
private:
immutable Config config;
OAuthSession session;
}
(I won't have time to work on this for the next days - if you have time and want to finish this, please do)
Thanks for this! I'll add this in a bit.
import ae.net.oauth.common;
Wow, I totally forgot I wrote that.
auto parameters = new UrlParameters[](1);
Any reason this has to be an array that I'm not seeing?
string queryString = parameters.map!(ps => ps.pairs.map!(p => session.encode(p.key) ~ "=" ~ session.encode(p.value))).joiner.join("&");
Does Twitter really expect the GET parameters to be encoded exactly the same as OAuth ones? I.e. wouldn't a normal encodeUrlParameters
suffice here?
You are very welcome. As you know I have realized that things happen a lot faster when you do most of the work :)
Any reason this has to be an array that I'm not seeing?
That's what your oAuth API expects.
Does Twitter really expect the GET parameters to be encoded exactly the same as OAuth ones? I.e. wouldn't a normal encodeUrlParameters suffice here?
Yeah, it took a lot of trial and error to figure out how to do it correctly. Note that encode is a specific version of this:
static alias oauthEncode = encodeUrlPart!(c => std.ascii.isAlphaNum(c) || c=='-' || c=='.' || c=='_' || c=='~');
That's what your oAuth API expects.
The method is variadic, so it can either take an array or any number of arguments of that type.
Hmm, I can't get it to work... just get 401 Unauthorized.
Are you sure the OAuth session strings are meant to be part of the configuration, and we aren't supposed to get them from some authentication endpoint?
Pushed a WIP: https://github.com/CyberShadow/DFeed/blob/master/src/dfeed/sinks/twitter.d
Yeah, here's how I tested it without running DFeed:
void main(string[] args)
{
import std.algorithm, std.array;
import std.net.curl;
import std.format;
import std.datetime;
import std.stdio;
import ae.net.oauth.common;
import ae.net.http.common;
import ae.net.ietf.url;
static struct Config
{
string consumerKey = "xxx";
string consumerKeySecret = "xxx";
string oauthAccessToken = "xxx";
string oauthAccessTokenSecret = "xxx";
}
Config config;
OAuthConfig oAuthConfig = {
consumerKey: config.consumerKey,
consumerSecret: config.consumerKeySecret
};
OAuthSession session = {
config: oAuthConfig, token:
config.oauthAccessToken,
tokenSecret: config.oauthAccessTokenSecret
};
auto parameters = new UrlParameters[](1);
parameters[0]["status"] = "%s by %s #dlang".format("title", "author");
string url = "https://api.twitter.com/1.1/statuses/update.json";
string queryString = parameters.map!(ps => ps.pairs.map!(p => session.encode(p.key) ~ "=" ~ session.encode(p.value))).joiner.join("&");
auto http = HTTP((url ~ "?" ~ queryString));
http.method = HTTP.Method.post;
http.addRequestHeader("Authorization", session.prepareRequest(url, "POST", parameters).oauthHeader);
http.verbose = true;
http.contentLength = 0;
http.perform();
}
Are you sure the OAuth session strings are meant to be part of the configuration, and we aren't supposed to get them from some authentication endpoint?
That's only if we would do OAuth for costumer - for sole API usage Twitter provides these "consumer keys".
OK, figured it out.
Deployed; hopefully it will work.
BTW, a much simpler way to do this would have been to plug in https://forum.dlang.org/feed/threads/announce into https://ifttt.com/connect/feed/twitter OSLT.
BTW, a much simpler way to do this would have been to plug in https://forum.dlang.org/feed/threads/announce into https://ifttt.com/connect/feed/twitter OSLT.
I made bad experiences with such services in the past. They tend to randomly break, limit the usage, or just start to charge you for it. For example, I setup Zapier for copying the ical calendars from the Meetup events to a global D calendar, but after a few months they reduced their "free" API usage to five Meetups. I thought you were the one usually pushing for things to be under our own control or open source.
Deployed; hopefully it will work.
Awesome! Thanks!
I made bad experiences with such services in the past. They tend to randomly break, limit the usage, or just start to charge you for it. For example, I setup Zapier for copying the ical calendars from the Meetup events to a global D calendar, but after a few months they reduced their "free" API usage to five Meetups.
Ouch.
I thought you were the one usually pushing for things to be under our own control or open source.
Yes, but Twitter already isn't, so how much worse can it get? :) (Especially for a low-importance task like here)
Works nicely: https://twitter.com/dlang_ng/status/959511178656575489 :)
Do you want to make a short announcement?
Eh. Feel free to if you feel there should be one, you did the initial implementation after all :)
On Fri, Feb 2, 2018 at 7:42 PM, Sebastian Wilzbach <notifications@github.com
wrote:
Works nicely: https://twitter.com/dlang_ng/status/959511178656575489 :)
Do you want to make a short announcement?
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/CyberShadow/DFeed/issues/97#issuecomment-362686101, or mute the thread https://github.com/notifications/unsubscribe-auth/AAJ0fk_u4Lj8GwbATYC1iuhYIAkuXUM-ks5tQ2UKgaJpZM4R1mNU .
From: https://forum.dlang.org/post/p4uubg$1rbl$1@digitalmars.com