CyberShadow / DFeed

D news aggregator, newsgroup client, web newsreader and IRC bot
http://forum.dlang.org/help#about
381 stars 35 forks source link

Tweet new threads in NG.announce #97

Closed wilzbach closed 6 years ago

wilzbach commented 6 years ago

From: https://forum.dlang.org/post/p4uubg$1rbl$1@digitalmars.com

<shorturl> #dlang I went ahead and created an account: https://twitter.com/dlang_ng and have send you the credentials. I will look now at how to do this best in DFeed, but maybe it's a simple change for you? </div> </div> <div class="comment"> <div class="user"> <a rel="noreferrer nofollow" target="_blank" href="https://github.com/wilzbach"><img src="https://avatars.githubusercontent.com/u/4370550?v=4" />wilzbach</a> commented <strong> 6 years ago</strong> </div> <div class="markdown-body"> <p>Here's a <code>TwitterSink</code> - <code>tweet</code> 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.</p> <pre><code class="language-d">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; }</code></pre> <p>(I won't have time to work on this for the next days - if you have time and want to finish this, please do)</p> </div> </div> <div class="comment"> <div class="user"> <a rel="noreferrer nofollow" target="_blank" href="https://github.com/CyberShadow"><img src="https://avatars.githubusercontent.com/u/160894?v=4" />CyberShadow</a> commented <strong> 6 years ago</strong> </div> <div class="markdown-body"> <p>Thanks for this! I'll add this in a bit.</p> <blockquote> <p><code>import ae.net.oauth.common;</code></p> </blockquote> <p>Wow, I totally forgot I wrote that.</p> <blockquote> <p><code>auto parameters = new UrlParameters[](1);</code></p> </blockquote> <p>Any reason this has to be an array that I'm not seeing?</p> <blockquote> <p><code>string queryString = parameters.map!(ps => ps.pairs.map!(p => session.encode(p.key) ~ "=" ~ session.encode(p.value))).joiner.join("&");</code></p> </blockquote> <p>Does Twitter really expect the GET parameters to be encoded exactly the same as OAuth ones? I.e. wouldn't a normal <code>encodeUrlParameters</code> suffice here?</p> </div> </div> <div class="comment"> <div class="user"> <a rel="noreferrer nofollow" target="_blank" href="https://github.com/wilzbach"><img src="https://avatars.githubusercontent.com/u/4370550?v=4" />wilzbach</a> commented <strong> 6 years ago</strong> </div> <div class="markdown-body"> <p>You are very welcome. As you know I have realized that things happen a lot faster when you do most of the work :)</p> <blockquote> <p>Any reason this has to be an array that I'm not seeing?</p> </blockquote> <p>That's what your oAuth API expects.</p> <blockquote> <p>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?</p> </blockquote> <p>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:</p> <pre><code class="language-d">static alias oauthEncode = encodeUrlPart!(c => std.ascii.isAlphaNum(c) || c=='-' || c=='.' || c=='_' || c=='~');</code></pre> </div> </div> <div class="comment"> <div class="user"> <a rel="noreferrer nofollow" target="_blank" href="https://github.com/CyberShadow"><img src="https://avatars.githubusercontent.com/u/160894?v=4" />CyberShadow</a> commented <strong> 6 years ago</strong> </div> <div class="markdown-body"> <blockquote> <p>That's what your oAuth API expects.</p> </blockquote> <p>The method is variadic, so it can either take an array or any number of arguments of that type.</p> </div> </div> <div class="comment"> <div class="user"> <a rel="noreferrer nofollow" target="_blank" href="https://github.com/CyberShadow"><img src="https://avatars.githubusercontent.com/u/160894?v=4" />CyberShadow</a> commented <strong> 6 years ago</strong> </div> <div class="markdown-body"> <p>Hmm, I can't get it to work... just get 401 Unauthorized.</p> <p>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?</p> <p>Pushed a WIP: <a href="https://github.com/CyberShadow/DFeed/blob/master/src/dfeed/sinks/twitter.d">https://github.com/CyberShadow/DFeed/blob/master/src/dfeed/sinks/twitter.d</a></p> </div> </div> <div class="comment"> <div class="user"> <a rel="noreferrer nofollow" target="_blank" href="https://github.com/wilzbach"><img src="https://avatars.githubusercontent.com/u/4370550?v=4" />wilzbach</a> commented <strong> 6 years ago</strong> </div> <div class="markdown-body"> <p>Yeah, here's how I tested it without running DFeed:</p> <pre><code class="language-d">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(); }</code></pre> </div> </div> <div class="comment"> <div class="user"> <a rel="noreferrer nofollow" target="_blank" href="https://github.com/wilzbach"><img src="https://avatars.githubusercontent.com/u/4370550?v=4" />wilzbach</a> commented <strong> 6 years ago</strong> </div> <div class="markdown-body"> <blockquote> <p>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?</p> </blockquote> <p>That's only if we would do OAuth for costumer - for sole API usage Twitter provides these "consumer keys".</p> </div> </div> <div class="comment"> <div class="user"> <a rel="noreferrer nofollow" target="_blank" href="https://github.com/CyberShadow"><img src="https://avatars.githubusercontent.com/u/160894?v=4" />CyberShadow</a> commented <strong> 6 years ago</strong> </div> <div class="markdown-body"> <p>OK, figured it out.</p> </div> </div> <div class="comment"> <div class="user"> <a rel="noreferrer nofollow" target="_blank" href="https://github.com/CyberShadow"><img src="https://avatars.githubusercontent.com/u/160894?v=4" />CyberShadow</a> commented <strong> 6 years ago</strong> </div> <div class="markdown-body"> <p>Deployed; hopefully it will work.</p> <p>BTW, a much simpler way to do this would have been to plug in <a href="https://forum.dlang.org/feed/threads/announce">https://forum.dlang.org/feed/threads/announce</a> into <a href="https://ifttt.com/connect/feed/twitter">https://ifttt.com/connect/feed/twitter</a> OSLT.</p> </div> </div> <div class="comment"> <div class="user"> <a rel="noreferrer nofollow" target="_blank" href="https://github.com/wilzbach"><img src="https://avatars.githubusercontent.com/u/4370550?v=4" />wilzbach</a> commented <strong> 6 years ago</strong> </div> <div class="markdown-body"> <blockquote> <p>BTW, a much simpler way to do this would have been to plug in <a href="https://forum.dlang.org/feed/threads/announce">https://forum.dlang.org/feed/threads/announce</a> into <a href="https://ifttt.com/connect/feed/twitter">https://ifttt.com/connect/feed/twitter</a> OSLT.</p> </blockquote> <p>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.</p> <blockquote> <p>Deployed; hopefully it will work.</p> </blockquote> <p>Awesome! Thanks!</p> </div> </div> <div class="comment"> <div class="user"> <a rel="noreferrer nofollow" target="_blank" href="https://github.com/CyberShadow"><img src="https://avatars.githubusercontent.com/u/160894?v=4" />CyberShadow</a> commented <strong> 6 years ago</strong> </div> <div class="markdown-body"> <blockquote> <p>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.</p> </blockquote> <p>Ouch.</p> <blockquote> <p>I thought you were the one usually pushing for things to be under our own control or open source.</p> </blockquote> <p>Yes, but Twitter already isn't, so how much worse can it get? :) (Especially for a low-importance task like here)</p> </div> </div> <div class="comment"> <div class="user"> <a rel="noreferrer nofollow" target="_blank" href="https://github.com/wilzbach"><img src="https://avatars.githubusercontent.com/u/4370550?v=4" />wilzbach</a> commented <strong> 6 years ago</strong> </div> <div class="markdown-body"> <p>Works nicely: <a href="https://twitter.com/dlang_ng/status/959511178656575489">https://twitter.com/dlang_ng/status/959511178656575489</a> :)</p> <p>Do you want to make a short announcement?</p> </div> </div> <div class="comment"> <div class="user"> <a rel="noreferrer nofollow" target="_blank" href="https://github.com/CyberShadow"><img src="https://avatars.githubusercontent.com/u/160894?v=4" />CyberShadow</a> commented <strong> 6 years ago</strong> </div> <div class="markdown-body"> <p>Eh. Feel free to if you feel there should be one, you did the initial implementation after all :)</p> <p>On Fri, Feb 2, 2018 at 7:42 PM, Sebastian Wilzbach <notifications@github.com</p> <blockquote> <p>wrote:</p> <p>Works nicely: <a href="https://twitter.com/dlang_ng/status/959511178656575489">https://twitter.com/dlang_ng/status/959511178656575489</a> :)</p> <p>Do you want to make a short announcement?</p> <p>— You are receiving this because you commented. Reply to this email directly, view it on GitHub <a href="https://github.com/CyberShadow/DFeed/issues/97#issuecomment-362686101">https://github.com/CyberShadow/DFeed/issues/97#issuecomment-362686101</a>, or mute the thread <a href="https://github.com/notifications/unsubscribe-auth/AAJ0fk_u4Lj8GwbATYC1iuhYIAkuXUM-ks5tQ2UKgaJpZM4R1mNU">https://github.com/notifications/unsubscribe-auth/AAJ0fk_u4Lj8GwbATYC1iuhYIAkuXUM-ks5tQ2UKgaJpZM4R1mNU</a> .</p> </blockquote> </div> </div> <div class="page-bar-simple"> </div> <div class="footer"> <ul class="body"> <li>© <script> document.write(new Date().getFullYear()) </script> Githubissues.</li> <li>Githubissues is a development platform for aggregating issues.</li> </ul> </div> <script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js"></script> <script src="/githubissues/assets/js.js"></script> <script src="/githubissues/assets/markdown.js"></script> <script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.4.0/build/highlight.min.js"></script> <script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.4.0/build/languages/go.min.js"></script> <script> hljs.highlightAll(); </script> </body> </html>