Skip to content

Commit ff1bb5c

Browse files
committed
Added support for querying user info.
1 parent ec2eec0 commit ff1bb5c

4 files changed

Lines changed: 250 additions & 24 deletions

File tree

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package WikiBot.APIcommands.Query;
2+
3+
import java.util.ArrayList;
4+
5+
import WikiBot.ContentRep.User;
6+
7+
/**
8+
* @Description
9+
* This command gets a list of a user's contributions.
10+
*
11+
* @RequiredRights
12+
* none
13+
*
14+
* @MediawikiSupport
15+
* Minimum:
16+
* 1.9+
17+
*
18+
* Specifics:
19+
* Support for certain properties was only introduced in later versions of mediawiki.
20+
* The support table is:
21+
* parsedcomment: 1.16+
22+
* size: 1.16+
23+
* sizediff: 1.20+
24+
* tags: 1.16+
25+
*/
26+
public class QueryUserContribs extends QueryList {
27+
public QueryUserContribs(User user, ArrayList<String> propertiesToGet, int uclimit) {
28+
super("Query user contribs", user.getLanguage(), "usercontribs", "json");
29+
30+
keys.add("ucuser");
31+
values.add(user.getUserName());
32+
keys.add("ucprop");
33+
values.add(compactArray(propertiesToGet, "|"));
34+
keys.add("uclimit");
35+
values.add("" + uclimit);
36+
unescapeText = false;
37+
unescapeHTML = false;
38+
39+
enforceMWVersion("1.9");
40+
41+
localEnforce(propertiesToGet);
42+
}
43+
44+
public void localEnforce(ArrayList<String> propertiesToGet) {
45+
if (propertiesToGet.contains("parsedcomment")) {
46+
enforceMWVersion("1.16");
47+
}
48+
if (propertiesToGet.contains("size")) {
49+
enforceMWVersion("1.16");
50+
}
51+
if (propertiesToGet.contains("sizediff")) {
52+
enforceMWVersion("1.20");
53+
}
54+
if (propertiesToGet.contains("tags")) {
55+
enforceMWVersion("1.16");
56+
}
57+
}
58+
}

src/main/java/WikiBot/APIcommands/Query/QueryUserInfo.java

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import java.util.ArrayList;
44

5+
import WikiBot.ContentRep.User;
6+
57
/**
68
* @Description
79
* This command queries a wiki for user information.
@@ -27,10 +29,24 @@
2729
*/
2830
public class QueryUserInfo extends QueryList {
2931

30-
public QueryUserInfo(String language, ArrayList<String> userNames, ArrayList<String> propertiesToGet) {
31-
super("Query user info", language, "users", "json");
32+
public QueryUserInfo(ArrayList<User> users, ArrayList<String> propertiesToGet) {
33+
super("Query user info", users.get(0).getLanguage(), "users", "json");
34+
35+
//Parse usernames into a MW friendly format.
36+
String parsedUserNames = "";
37+
for (int i = 0; i < users.size(); i++) {
38+
User user = users.get(i);
39+
40+
if (i != 0){
41+
parsedUserNames += "|";
42+
}
43+
44+
parsedUserNames += user.getUserName();
45+
}
46+
47+
//Finish initializing the API command
3248
keys.add("ususers");
33-
values.add(compactArray(userNames, "|"));
49+
values.add(parsedUserNames);
3450
keys.add("usprop");
3551
values.add(compactArray(propertiesToGet, "|"));
3652

src/main/java/WikiBot/ContentRep/Revision.java

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ public class Revision extends PageLocationContainer {
77
private String user;
88
private String comment;
99
private Date date;
10-
private String page = null;
10+
private String revContent = null;
1111
private ArrayList<String> flags;
1212

1313
public Revision(PageLocation pl_, String user_, String comment_, Date date_, ArrayList<String> flags_) {
@@ -18,8 +18,8 @@ public Revision(PageLocation pl_, String user_, String comment_, Date date_, Arr
1818
flags = flags_;
1919
}
2020

21-
public void setPageContent(String page_) {
22-
page = page_;
21+
public void setRevisionContent(String revContent_) {
22+
revContent = revContent_;
2323
}
2424

2525
public String getUser() {
@@ -65,9 +65,13 @@ public boolean isMinor() {
6565
public boolean isBot() {
6666
return flags.contains("bot");
6767
}
68+
69+
public boolean hasRevisionContent() {
70+
return revContent != null;
71+
}
6872

69-
public String getPage() {
70-
return page;
73+
public String getRevisionContent() {
74+
return revContent;
7175
}
7276

7377
@Override
@@ -76,10 +80,10 @@ public String toString() {
7680

7781
//Attach if the revision contains page contents or not.
7882
output = "(Revision";
79-
if (page != null) {
80-
output += " ; Page included";
83+
if (hasRevisionContent()) {
84+
output += " ; Content included";
8185
} else {
82-
output += " ; Page not included";
86+
output += " ; Content not included";
8387
}
8488
output += ") ";
8589

src/main/java/WikiBot/Core/GenericBot.java

Lines changed: 161 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -719,7 +719,7 @@ protected ImageInfo getImageInfo(PageLocation loc, ArrayList<String> propertyNam
719719
try {
720720
rootNode = mapper.readValue(serverOutput, JsonNode.class);
721721
} catch (IOException e1) {
722-
logError("Was expecting JSON, but did not recieve JSON from server.");
722+
logError("Was expecting JSON, but did not receive JSON from server.");
723723
return null;
724724
}
725725

@@ -888,22 +888,16 @@ protected ArrayList<UserInfo> getUserInfo(ArrayList<User> users, ArrayList<Strin
888888
chunkOfUsers = new ArrayList<User>(users.subList(i, users.size()));
889889
}
890890

891-
//Extract usernames from users
892-
ArrayList<String> chunkOfUserNames = new ArrayList<String>();
893-
for (User u : chunkOfUsers) {
894-
chunkOfUserNames.add(u.getUserName());
895-
}
896-
897891
//Query the server.
898-
String serverOutput = APIcommand(new QueryUserInfo(language, chunkOfUserNames, propertyNames));
892+
String serverOutput = APIcommand(new QueryUserInfo(chunkOfUsers, propertyNames));
899893

900894
// Read in the JSON!!!
901895
ObjectMapper mapper = new ObjectMapper();
902896
JsonNode rootNode = null;
903897
try {
904898
rootNode = mapper.readValue(serverOutput, JsonNode.class);
905899
} catch (IOException e1) {
906-
logError("Was expecting JSON, but did not recieve JSON from server.");
900+
logError("Was expecting JSON, but did not receive JSON from server.");
907901
return null;
908902
}
909903

@@ -1007,6 +1001,132 @@ protected ArrayList<UserInfo> getUserInfo(ArrayList<User> users, ArrayList<Strin
10071001
return toReturn;
10081002
}
10091003

1004+
/**
1005+
* Get the contributions of a user, up to a certain limit.
1006+
* @param users The user to query.
1007+
* @param depth The max amount of revisions, per user, to return.
1008+
* @return An ArrayList of a user's contributions.
1009+
*/
1010+
public ArrayList<Revision> getUserContribs(User user, int depth) {
1011+
ArrayList<User> users = new ArrayList<User>();
1012+
users.add(user);
1013+
return getUserContribs(users, depth).get(0);
1014+
}
1015+
1016+
/**
1017+
* Get the contributions of multiple users, up to a certain limit.
1018+
* @param users The users to query.
1019+
* @param depth The max amount of revisions, per user, to return.
1020+
* @return An ArrayList of ArrayList of Revision. In other words, it is a list of a users' list of contributions.
1021+
*/
1022+
public ArrayList<ArrayList<Revision>> getUserContribs(ArrayList<User> users, int depth) {
1023+
//Logging
1024+
String userLogMessage = "Getting contribs for users: ";
1025+
for (User u : users) {
1026+
userLogMessage += u.getUserName() + ", ";
1027+
}
1028+
1029+
logFine(userLogMessage);
1030+
1031+
//Method code below
1032+
ArrayList<ArrayList<Revision>> multiContribs = new ArrayList<ArrayList<Revision>>();
1033+
1034+
String language = users.get(0).getLanguage();
1035+
1036+
//For now, we only query certain properties.
1037+
ArrayList<String> properties = new ArrayList<String>();
1038+
properties.add("title");
1039+
properties.add("comment");
1040+
properties.add("timestamp");
1041+
properties.add("flags");
1042+
1043+
//Query users individually.
1044+
int maxQuerySize = Math.min(50, APIlimit);
1045+
for (int u = 0; u < users.size(); u++) {
1046+
//Get a user.
1047+
User user = users.get(u);
1048+
1049+
//Query user's contributions.
1050+
int rev = 0;
1051+
boolean moreRevisionsExist = true;
1052+
String queryContinue = null; // User for continuing queries.
1053+
while (rev < depth && moreRevisionsExist) {
1054+
//Query the server.
1055+
int querySize = -1;
1056+
if (rev + maxQuerySize < depth) {
1057+
querySize = maxQuerySize;
1058+
} else {
1059+
querySize = depth - rev;
1060+
}
1061+
APIcommand queryUserContribs = new QueryUserContribs(user, properties, querySize);
1062+
if (queryContinue != null) {
1063+
queryUserContribs.addParameter("ucstart", queryContinue);
1064+
}
1065+
1066+
String serverOutput = APIcommand(queryUserContribs);
1067+
1068+
1069+
// Read in the JSON!!!
1070+
ObjectMapper mapper = new ObjectMapper();
1071+
JsonNode rootNode = null;
1072+
try {
1073+
rootNode = mapper.readValue(serverOutput, JsonNode.class);
1074+
} catch (IOException e1) {
1075+
logError("Was expecting JSON, but did not receive JSON from server.");
1076+
return null;
1077+
}
1078+
1079+
//Parse JSON for contribs
1080+
ArrayList<Revision> contribs = new ArrayList<Revision>();
1081+
1082+
JsonNode queryNode = rootNode.findValue("query");
1083+
JsonNode userContribs = queryNode.findValue("usercontribs");
1084+
1085+
for (int contribID = 0; contribID < userContribs.size(); contribID++) {
1086+
JsonNode contrib = userContribs.get(contribID);
1087+
1088+
//Parse for revision info
1089+
String title = unescape(contrib.findValue("title").textValue());
1090+
PageLocation loc = new PageLocation(title, language);
1091+
String userName = user.getUserName();
1092+
String comment = unescape(contrib.findValue("comment").textValue());
1093+
Date date = createDate(unescape(contrib.findValue("timestamp").textValue()));
1094+
1095+
//Parse for flags
1096+
ArrayList<String> flags = new ArrayList<String>();
1097+
1098+
if (contrib.has("new")) {
1099+
flags.add("new");
1100+
}
1101+
if (contrib.has("top")) {
1102+
flags.add("top");
1103+
}
1104+
if (contrib.has("minor")) {
1105+
flags.add("minor");
1106+
}
1107+
1108+
//Package and ship the revision! Then have it sink due to a bunyip and cucumber sandwiches.
1109+
contribs.add(new Revision(loc, userName, comment, date, flags));
1110+
}
1111+
1112+
multiContribs.add(contribs);
1113+
1114+
rev += querySize;
1115+
1116+
//Parse for query continue
1117+
JsonNode queryContinueNode = rootNode.findValue("query-continue");
1118+
if (queryContinueNode == null) {
1119+
moreRevisionsExist = false;
1120+
} else {
1121+
JsonNode ucstartNode = queryContinueNode.findValue("ucstart");
1122+
queryContinue = ucstartNode.textValue();
1123+
}
1124+
}
1125+
}
1126+
1127+
return multiContribs;
1128+
}
1129+
10101130
/**
10111131
* Log into a wiki.
10121132
* @param user The username.
@@ -1138,7 +1258,7 @@ public String APIcommand(APIcommand command) {
11381258
}
11391259

11401260
if (textReturned.contains("<warnings>")) {
1141-
logError("Warnings were recieved when editing " + command.getTitle() + ".");
1261+
logError("Warnings were received when editing " + command.getTitle() + ".");
11421262
} else {
11431263
//Check other possibilities for errors/warnings being returned..
11441264
String errorMessage = null;
@@ -1171,15 +1291,15 @@ public String APIcommand(APIcommand command) {
11711291
}
11721292
} else if (command.getValue("format").equalsIgnoreCase("xml")) {
11731293
//We are handling XML output. We do not do anything.
1174-
logFinest("XML recieved.");
1294+
logFinest("XML received.");
11751295
if (textReturned.length() < 1000) {
11761296
logFinest("XML: " + textReturned);
11771297
} else {
11781298
logFinest("XML: " + textReturned.substring(0, 1000));
11791299
}
11801300
} else if (command.getValue("format").equalsIgnoreCase("php")) {
11811301
//We are handling PHP output. We do not do anything.
1182-
logFinest("PHP recieved.");
1302+
logFinest("PHP received.");
11831303
if (textReturned.length() < 1000) {
11841304
logFinest("PHP: " + textReturned);
11851305
} else {
@@ -1455,7 +1575,7 @@ private ArrayList<Revision> getRevisionsFromXML(String XMLdata, String openingTe
14551575
rev = new Revision(new PageLocation(forceTitle, mdm.getWikiPrefixFromURL(baseURL)), user, comment, date, flags);
14561576
}
14571577

1458-
rev.setPageContent(content);
1578+
rev.setRevisionContent(content);
14591579

14601580
//For eventual return.
14611581
output.add(rev);
@@ -1480,6 +1600,34 @@ public Date createDate(String text) {
14801600
return date;
14811601
}
14821602

1603+
/**
1604+
* Unescapes string literals and HTML.
1605+
* @param text The text to unescape.
1606+
* @return A String.
1607+
*/
1608+
private String unescape(String text) {
1609+
return unescape(text, true, true);
1610+
}
1611+
1612+
/**
1613+
* Unescapes text.
1614+
* @param text The text to unescape.
1615+
* @param unescapeText Unescapes string literals. Ex: \n, \s, \ u
1616+
* @param unescapeHTML Unescapes HTML text. Ex: & #039;
1617+
* @return A String.
1618+
*/
1619+
private String unescape(String text, boolean unescapeText, boolean unescapeHTML) {
1620+
String unescaped = text;
1621+
if (unescapeText) {
1622+
unescaped = StringEscapeUtils.unescapeJava(unescaped);
1623+
}
1624+
if (unescapeHTML) {
1625+
unescaped = StringEscapeUtils.unescapeHtml4(StringEscapeUtils.unescapeHtml4(unescaped));
1626+
}
1627+
1628+
return unescaped;
1629+
}
1630+
14831631
/**
14841632
* This method makes sure that the bot does not do particular actions too quickly.
14851633
*/

0 commit comments

Comments
 (0)