forked from CyberShadow/DFeed
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstackoverflow.d
More file actions
138 lines (120 loc) · 3.5 KB
/
stackoverflow.d
File metadata and controls
138 lines (120 loc) · 3.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/* Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016 Vladimir Panteleev <vladimir@thecybershadow.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
module stackoverflow;
import std.string;
import std.file;
import std.conv;
import std.datetime;
import ae.net.http.client;
import ae.utils.json;
import ae.utils.text;
import bitly;
import common;
import webpoller;
class StackOverflow : WebPoller
{
static struct Config
{
string tags;
string key;
int pollPeriod = 60;
}
this(Config config)
{
this.config = config;
super("StackOverflow-" ~ config.tags, config.pollPeriod);
}
private:
Config config;
class Question : Post
{
string title;
string author;
string url;
this(string title, string author, string url, SysTime time)
{
this.title = title;
this.author = author;
this.url = url;
this.time = time;
}
override void formatForIRC(void delegate(string) handler)
{
shortenURL(url, (string shortenedURL) {
handler(format("[StackOverflow] %s asked \"%s\": %s", filterIRCName(author), title, shortenedURL));
});
}
}
protected:
override void getPosts()
{
auto url = "http://api.stackexchange.com/2.2/questions?pagesize=10&order=desc&sort=creation&site=stackoverflow&tagged=" ~ config.tags ~
(config.key ? "&key=" ~ config.key : "");
httpGet(url, (string json) {
if (json == "<html><body><h1>408 Request Time-out</h1>\nYour browser didn't send a complete request in time.\n</body></html>\n")
{
log("Server reports request timeout");
return; // Temporary problem
}
if (json.contains("<title>We are Offline</title>"))
{
log("Server reports SO is offline");
return; // Temporary problem
}
struct JsonQuestionOwner
{
int reputation;
int user_id;
string user_type;
int accept_rate;
string profile_image;
string display_name;
string link;
}
struct JsonQuestion
{
string[] tags;
bool is_answered;
int answer_count, accepted_answer_id, favorite_count;
int closed_date;
string closed_reason;
int bounty_closes_date, bounty_amount;
string question_timeline_url, question_comments_url, question_answers_url;
int question_id;
int locked_date;
JsonQuestionOwner owner;
int creation_date, last_edit_date, last_activity_date;
int up_vote_count, down_vote_count, view_count, score;
bool community_owned;
string title, link;
}
struct JsonQuestions
{
JsonQuestion[] items;
bool has_more;
int quota_max, quota_remaining, backoff;
}
scope(failure) std.file.write("so-error.txt", json);
auto data = jsonParse!(JsonQuestions)(json);
Post[string] r;
foreach (q; data.items)
r[text(q.question_id)] = new Question(q.title, q.owner.display_name, format("http://stackoverflow.com/q/%d", q.question_id), SysTime(unixTimeToStdTime(q.creation_date)));
handlePosts(r);
}, (string error) {
handleError(error);
});
}
}