finished version 0.9

everything is working fine and can be used in testing environnement as it hasn't been tested long enough for being safely deployed in production environnement
This commit is contained in:
Louis Vallat 2019-04-25 17:24:49 +02:00
parent ae1765851b
commit 5c762ed6ee
25 changed files with 1431 additions and 365 deletions

3
.gitignore vendored
View File

@ -23,4 +23,5 @@
hs_err_pid*
/twitter_techsupportgore_bot/nbproject/private/
/twitter_techsupportgore_bot/build/
/twitter_techsupportgore_bot/dist/
/twitter_techsupportgore_bot/dist/
*.sqlite

View File

@ -7,8 +7,8 @@
<!-- the Compile on Save feature is turned off for the project. -->
<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
<!-- in the project's Project Properties dialog box.-->
<project name="twitter_techsupportgore_bot" default="default" basedir=".">
<description>Builds, tests, and runs the project twitter_techsupportgore_bot.</description>
<project name="TwitterTechSupportGoreBot" default="default" basedir=".">
<description>Builds, tests, and runs the project TwitterTechSupportGoreBot.</description>
<import file="nbproject/build-impl.xml"/>
<!--
@ -58,7 +58,7 @@
An example of overriding the target for project execution could look like this:
<target name="run" depends="twitter_techsupportgore_bot-impl.jar">
<target name="run" depends="TwitterTechSupportGoreBot-impl.jar">
<exec dir="bin" executable="launcher.exe">
<arg file="${dist.jar}"/>
</exec>

View File

@ -1,5 +1,5 @@
build.xml.data.CRC32=3d50692e
build.xml.script.CRC32=e61978ec
build.xml.data.CRC32=3680b253
build.xml.script.CRC32=9d82390b
build.xml.stylesheet.CRC32=8064a381@1.80.1.48
# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.

View File

@ -3,7 +3,7 @@ annotation.processing.enabled.in.editor=false
annotation.processing.processors.list=
annotation.processing.run.all.processors=true
annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output
application.title=twitter_techsupportgore_bot
application.title=TwitterTechSupportGoreBot
application.vendor=louis
build.classes.dir=${build.dir}/classes
build.classes.excludes=**/*.java,**/*.form
@ -25,16 +25,20 @@ debug.test.classpath=\
dist.archive.excludes=
# This directory is removed when the project is cleaned:
dist.dir=dist
dist.jar=${dist.dir}/twitter_techsupportgore_bot.jar
dist.jar=${dist.dir}/TwitterTechSupportGoreBot.jar
dist.javadoc.dir=${dist.dir}/javadoc
endorsed.classpath=
excludes=
file.reference.gson-2.8.5.jar=C:\\Users\\louis\\Documents\\libraries\\Java\\gson-2.8.5.jar
file.reference.kotlin-runtime.jar=nulllib\\kotlin-runtime.jar
file.reference.sqlite-jdbc-3.23.1.jar=C:\\Users\\louis\\Documents\\libraries\\Java\\sqlite-jdbc-3.23.1.jar
file.reference.twitter4j-core-4.0.7.jar=C:\\Users\\louis\\Documents\\libraries\\Java\\twitter4j-core-4.0.7.jar
includes=**
jar.compress=false
javac.classpath=\
${file.reference.gson-2.8.5.jar}
${file.reference.gson-2.8.5.jar}:\
${file.reference.twitter4j-core-4.0.7.jar}:\
${file.reference.sqlite-jdbc-3.23.1.jar}
# Space-separated list of extra javac options
javac.compilerargs=-Xlint:unchecked
javac.deprecation=false
@ -60,7 +64,7 @@ javadoc.use=true
javadoc.version=false
javadoc.windowtitle=
kotlinc.classpath=${file.reference.kotlin-runtime.jar}
main.class=twitter_techsupportgore_bot.Twitter_techsupportgore_bot
main.class=TwitterTechSupportGoreBot.TwitterTechSupportGoreBot
manifest.file=manifest.mf
meta.inf.dir=${src.dir}/META-INF
mkdist.disabled=false

View File

@ -3,7 +3,7 @@
<type>org.netbeans.modules.java.j2seproject</type>
<configuration>
<data xmlns="http://www.netbeans.org/ns/j2se-project/3">
<name>twitter_techsupportgore_bot</name>
<name>TwitterTechSupportGoreBot</name>
<source-roots>
<root id="src.dir"/>
</source-roots>

View File

@ -0,0 +1,23 @@
################################################################################
# REDDIT SETTINGS #
################################################################################
subreddit=techsupportgore
delay=30
sqlite_db_name=techsupportgore_twitterbot
clear_database=N
reddit_posts_limit=1
reddit_posts_sorting_order=new
################################################################################
# MISCELLANEOUS SETTINGS #
################################################################################
working_directory=data
max_text_length=280
################################################################################
# TWITTER API SETTINGS #
################################################################################
twitterAPI_consumerKey=
twitterAPI_consumerSecret=
twitterAPI_accessToken=
twitterAPI_accessSecret=

View File

@ -0,0 +1,92 @@
/*
* Copyright (C) 2019 louis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package TwitterTechSupportGoreBot;
import java.io.File;
import java.util.Properties;
import TwitterTechSupportGoreBot.exceptions.*;
import java.io.FileInputStream;
import java.io.IOException;
/**
* A config file reader.
*
* @author louis
*/
public final class ConfigFileReader {
/**
* The config file name.
*/
private static final String CONFIGFILE = "settings.conf";
/**
* The properties object.
*/
private final Properties prop = new Properties();
/**
* Create a new config file reader that automatically reads the config file.
*
* @throws NoSuchFile
* @throws NotSufficientRights
*/
public ConfigFileReader() throws NoSuchFile, NotSufficientRights {
readConfigFile();
}
/**
* Read the config file.
*
* @throws TwitterTechSupportGoreBot.exceptions.NotSufficientRights
* @throws TwitterTechSupportGoreBot.exceptions.NoSuchFile
*/
public void readConfigFile() throws NoSuchFile, NotSufficientRights {
if (!new File(CONFIGFILE).exists()) {
throw new NoSuchFile(
"[!] The config file " + CONFIGFILE + " doesn't exists.");
} else if (!new File(CONFIGFILE).canRead()) {
throw new NotSufficientRights(
"[!] Can't read the config file " + CONFIGFILE + ".");
}
try {
prop.load(new FileInputStream(new File(CONFIGFILE)));
} catch (IOException ex) {
System.out.println(
"[!] Error on loading the config file.");
System.out.println("[!] " + ex.getMessage());
}
}
/**
* Get the property for an id.
*
* @param id the property id.
* @return the properties.
*
* @throws TwitterTechSupportGoreBot.exceptions.NoSuchProperty
*/
public String getProperties(String id) throws NoSuchProperty {
if (!this.prop.containsKey(id)) {
throw new NoSuchProperty(
"[!] The property " + id + " is not defined in the config "
+ "file. Define it in " + CONFIGFILE + " and try again.");
}
return this.prop.getProperty(id);
}
}

View File

@ -0,0 +1,532 @@
/*
* Copyright (C) 2019 louis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package TwitterTechSupportGoreBot;
import TwitterTechSupportGoreBot.redditHandler.RedditPostImage;
import TwitterTechSupportGoreBot.redditHandler.RedditPost;
import TwitterTechSupportGoreBot.redditHandler.RedditPostText;
import TwitterTechSupportGoreBot.redditHandler.RedditPostLink;
import TwitterTechSupportGoreBot.redditHandler.RedditPostVideo;
import TwitterTechSupportGoreBot.socialMediaHandler.*;
import TwitterTechSupportGoreBot.redditHandler.*;
import TwitterTechSupportGoreBot.exceptions.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* Singleton Hypervisor. This is the object that does everything. So we want it
* to be unique. That's why it's a singleton.
*
* @author louis
*/
public class Hypervisor {
/**
* Working folder.
*/
private String workingDirectory;
/**
* SQLITE database for saving the already parsed posts.
*/
private final String sqliteDatabase;
/**
* SQLITE table name.
*/
private final String tableName;
/**
* All the reddit posts parsed.
*/
private final HashMap<String, RedditPost> redditPosts;
/**
* All the already shared posts, so there are no boule shares.
*/
private final HashMap<String, RedditPost> alreadySharedPosts;
/**
* Delay between two scans, in seconds.
*/
private final int delay;
/**
* Minimum of all the social medias text size.
*/
private final int maxLength;
/**
* Subreddit to extract info from.
*/
private final String subreddit;
/**
* The singleton.
*/
private static Hypervisor SINGLETON = null;
/**
* All the social medias to post our reddit content.
*/
private final ArrayList<SocialMediaPoster> socialMedias;
/**
* Connection to the SQLITE database.
*/
private final Connection connexion;
/**
* RedditExtractor.
*/
private final RedditExtractor myRedditExtractor;
/**
* Private constructor so nobody except this obect can build this object.
*/
private Hypervisor()
throws NotSufficientRights, ClassNotFoundException,
SQLException, IOException, NoSuchFile, NoSuchProperty, NoSuchOrder {
Class.forName("org.sqlite.JDBC");
System.out.println("[+] Creating Hypervisor.");
ConfigFileReader reader = new ConfigFileReader();
this.subreddit = reader.getProperties("subreddit");
this.tableName = reader.getProperties("subreddit");
this.delay = Integer.valueOf(reader.getProperties("delay"));
this.sqliteDatabase = reader.getProperties("sqlite_db_name");
this.socialMedias = new ArrayList<>();
this.redditPosts = new HashMap<>();
this.workingDirectory = reader.getProperties("working_directory");
setupTheBotDirectory();
this.connexion = DriverManager.getConnection("jdbc:sqlite:"
+ this.workingDirectory + File.separator + this.sqliteDatabase);
this.myRedditExtractor = new RedditExtractor(subreddit);
this.alreadySharedPosts = new HashMap<>();
if ("Y".equals(reader.getProperties("clear_database"))) {
clearDatabase();
}
this.maxLength = Integer.valueOf(reader.getProperties("max_text_length"));
load();
System.out.println("[+] Hypervisor created successfully.");
}
/**
* Get the Singleton Hypervisor instance.
*
* @return the instance.
*
* @throws java.lang.ClassNotFoundException
* @throws TwitterTechSupportGoreBot.exceptions.NotSufficientRights
* @throws java.sql.SQLException
* @throws java.io.IOException
* @throws TwitterTechSupportGoreBot.exceptions.NoSuchFile
* @throws TwitterTechSupportGoreBot.exceptions.NoSuchProperty
* @throws TwitterTechSupportGoreBot.exceptions.NoSuchOrder
*/
public static Hypervisor getSingleton()
throws ClassNotFoundException, NotSufficientRights,
SQLException, IOException, NoSuchFile, NoSuchProperty, NoSuchOrder {
if (SINGLETON == null) {
SINGLETON = new Hypervisor();
}
return SINGLETON;
}
/**
* Main loop for the program. This is where everything happens.
*
* @throws java.lang.ClassNotFoundException
* @throws java.sql.SQLException
* @throws java.lang.InterruptedException
*/
public void run()
throws ClassNotFoundException, SQLException,
InterruptedException {
System.out.println("[+] Hypervisor is now running.");
for (;;) {
for (RedditPost post : myRedditExtractor.getRedditPosts()) {
computeRedditPost(post);
}
save();
System.out.println(
"[*] Hypervisor is waiting for " + this.delay + "seconds.");
Thread.sleep(this.delay * 1000);
}
}
/**
* Load all the reddit posts from the database.
*
* @throws ClassNotFoundException
* @throws SQLException
*/
private void load() throws ClassNotFoundException, SQLException {
System.out.println("[*] Loading the Reddit posts from database.");
createTable();
PreparedStatement recherche = this.connexion.prepareStatement(
"SELECT * FROM " + this.tableName + ";");
try (ResultSet res = recherche.executeQuery()) {
while (res.next()) {
RedditPost post
= "image".equals(res.getString("postType"))
? new RedditPostImage(res.getString("postId"),
res.getString("title"),
res.getBoolean("quarantine"),
res.getDouble("score"),
res.getString("postHint"),
res.getBoolean("crosspostable"),
res.getBoolean("over18"),
res.getString("author"),
res.getString("permalink"),
res.getBoolean("spoiler"),
res.getString("url"))
: "link".equals(res.getString("postType"))
? new RedditPostLink(res.getString("postId"),
res.getString("title"),
res.getBoolean("quarantine"),
res.getDouble("score"),
res.getString("postHint"),
res.getBoolean("crosspostable"),
res.getBoolean("over18"),
res.getString("author"),
res.getString("permalink"),
res.getBoolean("spoiler"),
res.getString("url"))
: "video".equals(res.getString("postType"))
? new RedditPostVideo(res.getString("postId"),
res.getString("title"),
res.getBoolean("quarantine"),
res.getDouble("score"),
res.getString("postHint"),
res.getBoolean("crosspostable"),
res.getBoolean("over18"),
res.getString("author"),
res.getString("permalink"),
res.getBoolean("spoiler"),
res.getString("url"))
: new RedditPostText(res.getString("postId"),
res.getString("title"),
res.getBoolean("quarantine"),
res.getDouble("score"),
res.getString("postHint"),
res.getBoolean("crosspostable"),
res.getBoolean("over18"),
res.getString("author"),
res.getString("permalink"),
res.getBoolean("spoiler"),
res.getString("url"));
this.redditPosts.put(post.getPostId(), post);
if (res.getBoolean("shared")) {
this.alreadySharedPosts.put(post.getPostId(), post);
}
}
}
System.out.println("[*] Loading done. "
+ this.redditPosts.size() + " posts loaded. "
+ this.alreadySharedPosts.size() + " posts already shared.");
}
/**
* Clear the database by dropping it.
*
* @throws ClassNotFoundException
* @throws SQLException
*/
private void clearDatabase() throws ClassNotFoundException, SQLException {
System.out.println("[*] Clearing the database.");
PreparedStatement stmt = connexion.prepareStatement(""
+ "DROP TABLE IF EXISTS " + this.tableName + ";"
);
stmt.execute();
createTable();
System.out.println("[*] The database has been cleared successfully.");
}
/**
* Save all the reddit posts to the database.
*
* @throws ClassNotFoundException
* @throws SQLException
*/
private void save() throws ClassNotFoundException, SQLException {
createTable();
for (String postId : this.redditPosts.keySet()) {
RedditPost current = this.redditPosts.get(postId);
if (!isInDatabase(postId)) {
PreparedStatement ajout = this.connexion.prepareStatement(
"INSERT INTO " + this.tableName
+ "("
+ "postType, "
+ "postId, "
+ "title, "
+ "quarantine, "
+ "score, "
+ "postHint, "
+ "crosspostable, "
+ "over18, "
+ "author, "
+ "permalink, "
+ "spoiler, "
+ "url, "
+ "shared"
+ ") "
+ "VALUES "
+ "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"
);
ajout.setString(1,
current.isImage() ? "image"
: current.isLink() ? "link"
: current.isText() ? "text"
: "video");
ajout.setString(2, current.getPostId());
ajout.setString(3, current.getTitle());
ajout.setBoolean(4, current.isQuarantine());
ajout.setDouble(5, current.getScore());
ajout.setString(6, current.getPostHint());
ajout.setBoolean(7, current.isCrosspostable());
ajout.setBoolean(8, current.isOver18());
ajout.setString(9, current.getAuthor());
ajout.setString(10, current.getPermalink());
ajout.setBoolean(11, current.isSpoiler());
ajout.setString(12, current.getUrl());
ajout.setBoolean(13, this.alreadySharedPosts
.containsKey(current.getPostId()));
ajout.execute();
}
}
}
/**
* Check if a post is in database.
*
* @param postId the post id
* @return if the post is in the database
*
* @throws SQLException
* @throws ClassNotFoundException
*/
private boolean isInDatabase(String postId)
throws SQLException, ClassNotFoundException {
PreparedStatement recherche = this.connexion.prepareStatement(
"SELECT * FROM " + this.tableName + " "
+ "WHERE postId = '" + postId + "'");
try (ResultSet resultats = recherche.executeQuery()) {
return resultats.next();
}
}
/**
* Set a post as shared onsocial networks.
*
* @param postId the post id
*
* @throws SQLException
* @throws ClassNotFoundException
*/
private void setPostAsShared(String postId)
throws SQLException, ClassNotFoundException {
System.out.println("[+] Post \""
+ this.redditPosts.get(postId).getTitle()
+ "\" has been shared successfully.");
this.alreadySharedPosts.put(postId, this.redditPosts.get(postId));
if (isInDatabase(postId)) {
PreparedStatement update = this.connexion.prepareStatement(
"UPDATE " + this.tableName + " "
+ "SET shared=? "
+ "WHERE postId = '" + postId + "';"
);
update.setBoolean(1, true);
update.execute();
}
}
/**
* Create our working table if it doesn't exists yet.
*
* @throws SQLException
* @throws ClassNotFoundException
*/
private void createTable() throws SQLException, ClassNotFoundException {
PreparedStatement stmt = connexion.prepareStatement(""
+ "CREATE TABLE IF NOT EXISTS " + this.tableName + " "
+ "("
+ "id INTEGER PRIMARY KEY AUTOINCREMENT,"
+ "postType TEXT, "
+ "postId TEXT UNIQUE, "
+ "title TEXT, "
+ "quarantine BOOLEAN, "
+ "score DOUBLE, "
+ "postHint TEXT, "
+ "crosspostable BOOLEAN, "
+ "over18 BOOLEAN, "
+ "author TEXT, "
+ "permalink TEXT, "
+ "spoiler BOOLEAN, "
+ "url TEXT, "
+ "shared BOOLEAN"
+ ");"
);
stmt.execute();
}
/**
* Compute a given reddit post.
*
* @param r the reddit post to compute.
*
* @throws ClassNotFoundException
* @throws SQLException
*/
private void computeRedditPost(RedditPost r)
throws SQLException, ClassNotFoundException {
if (!this.alreadySharedPosts.containsKey(r.getPostId())
&& !r.isQuarantine()) {
System.out.println(
"[*] Computing the post \"" + r.getTitle() + "\"");
this.redditPosts.put(r.getPostId(), r);
if (r.hasMediaUrl()) {
String fileName = saveImage(r.getUrl());
socialMedias.forEach((s) -> {
long postRef = s.postImage(
formatPost(r.getTitle()), fileName);
if (postRef != 0 && postRef != -1) {
s.replyText(formatPost("from /u/" + r.getAuthor() + " "
+ "on /r/" + this.subreddit + " "
+ "at link : https://www.reddit.com"
+ r.getPermalink()), postRef);
}
});
deleteFile(fileName);
}
setPostAsShared(r.getPostId());
}
}
/**
* Format a given text for being posted on the different social networks.
*
* @param text the text to format
* @return the formatted text
*/
private String formatPost(String text) {
return text.length() >= this.maxLength
? text.substring(0, this.maxLength - 4) + "..."
: text;
}
/**
* Save Image from URL. Modified version of the code that can be found at
* https://www.programcreek.com/2012/12/download-image-from-url-in-java/
*
* @param imageUrl the image URL.
*/
private String saveImage(String imageUrl) {
System.out.println("[+] Dowloading image " + imageUrl + ".");
try {
URL url = new URL(imageUrl);
String fileName = url.getFile();
String destName = workingDirectory + fileName.substring(
fileName.lastIndexOf("/"), fileName.lastIndexOf("?"));
InputStream is = url.openStream();
OutputStream os = new FileOutputStream(destName);
byte[] b = new byte[2048];
int length;
while ((length = is.read(b)) != -1) {
os.write(b, 0, length);
}
is.close();
os.close();
System.out.println(
"[+] Image " + destName + " dowloaded successfully.");
return destName;
} catch (IOException ex) {
System.err.println("[!] IOException : " + ex.getMessage());
System.exit(1);
}
return null;
}
/**
* Delete a file.
*
* @param filePath the path to the file we want to delete.
*/
private void deleteFile(String filePath) {
File f = new File(filePath);
f.delete();
System.out.println("[*] File " + filePath + " deleted.");
}
/**
* Set working directory.
*
* @param tempDir the path to the working directory.
*
* @throws TwitterTechSupportGoreBot.exceptions.NotSufficientRights
*/
public void setWorkDir(String tempDir) throws NotSufficientRights {
this.workingDirectory = tempDir;
setupTheBotDirectory();
}
/**
* Get the working directory.
*
* @return the working directory path.
*/
public String getWorkDir() {
return workingDirectory;
}
/**
* Setup the bot.
*
* @throws NotSufficientRights
*/
private void setupTheBotDirectory() throws NotSufficientRights {
File f = new File(workingDirectory);
f.mkdir();
if (!f.canRead() || !f.canWrite()) {
throw new NotSufficientRights(
"[!] This program does not have the sufficient "
+ "rights on the folder \"" + workingDirectory + "\".");
}
}
/**
* Add a social media to post content to.
*
* @param s the social media
*/
public void addSocialMedia(SocialMediaPoster s) {
this.socialMedias.add(s);
}
}

View File

@ -0,0 +1,62 @@
/*
* Copyright (C) 2019 louis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package TwitterTechSupportGoreBot;
import TwitterTechSupportGoreBot.exceptions.NoSuchFile;
import TwitterTechSupportGoreBot.exceptions.NoSuchOrder;
import TwitterTechSupportGoreBot.exceptions.NoSuchProperty;
import TwitterTechSupportGoreBot.socialMediaHandler.*;
import TwitterTechSupportGoreBot.exceptions.NotSufficientRights;
import java.io.IOException;
import java.sql.SQLException;
/**
* This is where everything begins.
*
* @author louis
*/
public class TwitterTechSupportGoreBot {
/**
* Launch the Hypervisor.
*
* @param args command line arguments
*
* @throws ClassNotFoundException
* @throws NotSufficientRights
* @throws SQLException
* @throws IOException
* @throws InterruptedException
* @throws NoSuchFile
* @throws NoSuchProperty
* @throws NoSuchOrder
*/
public static void main(String[] args) throws
ClassNotFoundException,
NotSufficientRights,
SQLException,
IOException,
InterruptedException,
NoSuchFile,
NoSuchProperty,
NoSuchOrder {
Hypervisor master = Hypervisor.getSingleton();
master.addSocialMedia(new TwitterBot());
master.run();
}
}

View File

@ -0,0 +1,41 @@
/*
* Copyright (C) 2019 louis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package TwitterTechSupportGoreBot.exceptions;
/**
* Exception throwable when there are no file at a given path.
*
* @author louis
*/
public class NoSuchFile extends Exception {
/**
* Creates a new instance of <code>NoSuchFile</code> without detail message.
*/
public NoSuchFile() {
}
/**
* Constructs an instance of <code>NoSuchFile</code> with the specified
* detail message.
*
* @param msg the detail message.
*/
public NoSuchFile(String msg) {
super(msg);
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright (C) 2019 louis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package TwitterTechSupportGoreBot.exceptions;
/**
* Exception throwable when a sort order isn't a correct one.
*
* @author louis
*/
public class NoSuchOrder extends Exception {
/**
* Creates a new instance of <code>NoSuchOrder</code> without detail
* message.
*/
public NoSuchOrder() {
}
/**
* Constructs an instance of <code>NoSuchOrder</code> with the specified
* detail message.
*
* @param msg the detail message.
*/
public NoSuchOrder(String msg) {
super(msg);
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright (C) 2019 louis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package TwitterTechSupportGoreBot.exceptions;
/**
* Exception throwable when there are no property in the config file for a given
* setting name/id.
*
* @author louis
*/
public class NoSuchProperty extends Exception {
/**
* Creates a new instance of <code>NoSuchProperty</code> without detail
* message.
*/
public NoSuchProperty() {
}
/**
* Constructs an instance of <code>NoSuchProperty</code> with the specified
* detail message.
*
* @param msg the detail message.
*/
public NoSuchProperty(String msg) {
super(msg);
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright (C) 2019 louis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package TwitterTechSupportGoreBot.exceptions;
/**
* Exception throwable when the rights on a folder or a file aren't sufficient.
*
* @author louis
*/
public class NotSufficientRights extends Exception {
/**
* Creates a new instance of <code>NotSufficientRights</code> without detail
* message.
*/
public NotSufficientRights() {
}
/**
* Constructs an instance of <code>NotSufficientRights</code> with the
* specified detail message.
*
* @param msg the detail message.
*/
public NotSufficientRights(String msg) {
super(msg);
}
}

View File

@ -0,0 +1,218 @@
/*
* Copyright (C) 2019 louis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package TwitterTechSupportGoreBot.redditHandler;
import TwitterTechSupportGoreBot.exceptions.NoSuchFile;
import TwitterTechSupportGoreBot.exceptions.NoSuchOrder;
import TwitterTechSupportGoreBot.exceptions.NoSuchProperty;
import TwitterTechSupportGoreBot.exceptions.NotSufficientRights;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashSet;
/**
* Reddit extractor object.
*
* @author louis
*/
public final class RedditExtractor {
/**
* Subreddit to extract infos from.
*/
private final SubReddit sub;
/**
* Main Constructor.
*
* @param subreddit Subreddit name. Just after /r/
*
* @throws IOException
* @throws TwitterTechSupportGoreBot.exceptions.NoSuchProperty
* @throws TwitterTechSupportGoreBot.exceptions.NoSuchFile
* @throws TwitterTechSupportGoreBot.exceptions.NotSufficientRights
* @throws NoSuchOrder
*/
public RedditExtractor(String subreddit)
throws IOException, NoSuchProperty, NoSuchFile,
NotSufficientRights, NoSuchOrder {
if (!doesSubredditExists(subreddit)) {
throw new MalformedURLException("This subreddit ("
+ subreddit + ") does not exist.");
} else {
this.sub = new SubReddit(subreddit);
}
}
/**
* Check if a subreddit exists.
*
* @param subredditName
* @return if a subreddit exists
*/
public boolean doesSubredditExists(String subredditName) {
System.out.println("[*] Checking if subreddit /r/" + subredditName
+ " exists.");
return ((new JsonParser()
.parse(getJsonFromURL(""
+ "https://www.reddit.com/api/search_reddit_names.json"
+ "?query=" + subredditName + "&exact=true"))
.getAsJsonObject().get("names").getAsJsonArray()
.size()) >= 1);
}
/**
* Obtain the subreddit JSON response.
*
* @return the JSON from the REDDIT API.
*/
public String getSubredditJson() {
return getJsonFromURL(this.sub.getJsonURL());
}
/**
* Get JSON from URL.
*
* @param URL the JSON url.
* @return the JSON data as a String from the given URL.
*/
public String getJsonFromURL(String URL) {
try {
System.out.println("[+] Obtaining JSON from URL " + URL + ".");
HttpURLConnection con;
URL myurl = new URL(URL);
con = (HttpURLConnection) myurl.openConnection();
try {
con.setRequestMethod("GET");
con.setRequestProperty("User-Agent", "Mozilla 5.0 (Windows; U; "
+ "Windows NT 5.1; en-US; rv:1.8.0.11) ");
StringBuilder response;
try (BufferedReader in = new BufferedReader(
new InputStreamReader(con.getInputStream()))) {
String line;
response = new StringBuilder();
while ((line = in.readLine()) != null) {
response.append(line);
response.append(System.lineSeparator());
}
return response.toString();
}
} finally {
con.disconnect();
}
} catch (IOException e) {
System.err.println("[!] IOException: " + e.getMessage());
System.out.println("[!] Retrying...");
return getJsonFromURL(URL);
}
}
/**
* Get Reddit's subreddit posts.
*
* @return a treeset of all the reddit posts parsed.
*/
public HashSet<RedditPost> getRedditPosts() {
try {
HashSet<RedditPost> set = new HashSet<>();
String jsonResponse = getSubredditJson();
JsonObject objet = new JsonParser().parse(jsonResponse)
.getAsJsonObject();
JsonObject data = new JsonParser().parse(objet.get("data")
.toString()).getAsJsonObject();
JsonArray children = new JsonParser().parse(data.get("children")
.toString()).getAsJsonArray();
for (int i = 0; i < children.size(); i++) {
JsonObject child = new JsonParser().parse(children.get(i)
.toString()).getAsJsonObject();
JsonObject childData = new JsonParser().parse(child.get("data")
.toString()).getAsJsonObject();
if (childData.get("id").toString() != null
&& !childData.get("quarantine").getAsBoolean()
&& childData.get("url").getAsString() != null) {
String id = childData.get("id").toString();
String title = childData.get("title") != null
? childData.get("title").toString()
: this.sub.getName();
title = title.replace("\"", "").replace("\\", "\"");
String author = childData.get("author") != null
? childData.get("author").toString() : "anonymous";
author = author.replace("\"", "");
boolean quarantine = childData.get("quarantine")
.getAsBoolean();
double score = childData.get("score").getAsDouble();
String postHint = childData.get("post_hint").getAsString();
boolean crosspostable = !childData.get("is_crosspostable")
.getAsBoolean();
boolean over18 = childData.get("over_18").getAsBoolean();
String url;
try {
JsonObject preview = new JsonParser().parse(childData
.get("preview").toString()).getAsJsonObject();
JsonArray previewImages = new JsonParser().parse(preview
.get("images").toString()).getAsJsonArray();
JsonObject source = new JsonParser().parse(previewImages
.get(0).toString()).getAsJsonObject();
JsonObject urlSrc = new JsonParser().parse(source
.get("source").toString()).getAsJsonObject();
url = urlSrc.get("url").toString().replace("amp;", "")
.replace("\"", "");
} catch (NullPointerException n) {
url = childData.get("url").getAsString();
}
String permalink = childData.get("permalink").getAsString();
boolean spoiler = childData.get("spoiler").getAsBoolean();
if (postHint.contains("video")) {
set.add(new RedditPostVideo(
id, title, quarantine, score, postHint,
crosspostable, over18, author,
permalink, spoiler, url));
} else if ("link".equals(postHint)) {
set.add(new RedditPostLink(
id, title, quarantine, score, postHint,
crosspostable, over18, author,
permalink, spoiler, url));
} else if ("text".equals(postHint)) {
set.add(new RedditPostText(
id, title, quarantine, score, postHint,
crosspostable, over18, author,
permalink, spoiler, url));
} else if ("image".equals(postHint)) {
set.add(new RedditPostImage(
id, title, quarantine, score, postHint,
crosspostable, over18, author,
permalink, spoiler, url));
}
}
}
return set;
} catch (JsonSyntaxException e) {
System.err.println("[!] JsonSyntaxException: " + e.getMessage());
System.exit(1);
}
return null;
}
}

View File

@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package twitter_techsupportgore_bot.reddit_handler;
package TwitterTechSupportGoreBot.redditHandler;
/**
* This is an abstract class to define all RedditPosts.
@ -93,7 +93,9 @@ public abstract class RedditPost {
* @param spoiler is this post a spoiler?
* @param url post's media url
*/
public RedditPost(String id, String title, boolean quarantine, double score, String postHint, boolean crosspostable, boolean over18, String author, String permalink, boolean spoiler, String url) {
public RedditPost(String id, String title, boolean quarantine, double score,
String postHint, boolean crosspostable, boolean over18,
String author, String permalink, boolean spoiler, String url) {
this.postId = id;
this.title = title;
this.quarantine = quarantine;
@ -109,6 +111,7 @@ public abstract class RedditPost {
/**
* Get post's URL.
*
* @return the post's media URL.
*/
public String getUrl() {
@ -117,12 +120,13 @@ public abstract class RedditPost {
/**
* Check if the Media URL is correct.
*
* @return if the URL is correct.
*/
public boolean hasMediaUrl() {
return url.contains(".jpg") || url.contains(".png");
}
/**
* Get post's title.
*

View File

@ -14,20 +14,21 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package twitter_techsupportgore_bot.reddit_handler;
package TwitterTechSupportGoreBot.redditHandler;
/**
* Reddit image post object representation.
*
* @author louis
*/
public class RedditPostImage extends RedditPost {
public RedditPostImage(String id, String title, boolean quarantine,
double score, String postHint, boolean crosspostable,
public RedditPostImage(String id, String title, boolean quarantine,
double score, String postHint, boolean crosspostable,
boolean over18, String author, String permalink, boolean spoiler, String url) {
super(id, title, quarantine, score, postHint, crosspostable, over18, author, permalink, spoiler, url);
}
@Override
public boolean isImage() {
return true;

View File

@ -14,10 +14,11 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package twitter_techsupportgore_bot.reddit_handler;
package TwitterTechSupportGoreBot.redditHandler;
/**
*
* Reddit link post object representation.
*
* @author louis
*/
public class RedditPostLink extends RedditPost {

View File

@ -14,20 +14,21 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package twitter_techsupportgore_bot.reddit_handler;
package TwitterTechSupportGoreBot.redditHandler;
/**
* Reddit text post object representation.
*
* @author louis
*/
public class RedditPostText extends RedditPost {
public RedditPostText(String id, String title, boolean quarantine,
double score, String postHint, boolean crosspostable,
public RedditPostText(String id, String title, boolean quarantine,
double score, String postHint, boolean crosspostable,
boolean over18, String author, String permalink, boolean spoiler, String url) {
super(id, title, quarantine, score, postHint, crosspostable, over18, author, permalink, spoiler, url);
}
@Override
public boolean isImage() {
return false;

View File

@ -14,20 +14,21 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package twitter_techsupportgore_bot.reddit_handler;
package TwitterTechSupportGoreBot.redditHandler;
/**
* Reddit video post object representation.
*
* @author louis
*/
public class RedditPostVideo extends RedditPost{
public RedditPostVideo(String id, String title, boolean quarantine,
double score, String postHint, boolean crosspostable,
public class RedditPostVideo extends RedditPost {
public RedditPostVideo(String id, String title, boolean quarantine,
double score, String postHint, boolean crosspostable,
boolean over18, String author, String permalink, boolean spoiler, String url) {
super(id, title, quarantine, score, postHint, crosspostable, over18, author, permalink, spoiler, url);
}
}
@Override
public boolean isImage() {
return false;
@ -47,5 +48,5 @@ public class RedditPostVideo extends RedditPost{
public boolean isLink() {
return false;
}
}

View File

@ -14,14 +14,21 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package twitter_techsupportgore_bot.reddit_handler;
package TwitterTechSupportGoreBot.redditHandler;
import TwitterTechSupportGoreBot.ConfigFileReader;
import TwitterTechSupportGoreBot.exceptions.NoSuchFile;
import TwitterTechSupportGoreBot.exceptions.NoSuchOrder;
import TwitterTechSupportGoreBot.exceptions.NoSuchProperty;
import TwitterTechSupportGoreBot.exceptions.NotSufficientRights;
import java.util.Arrays;
/**
* A subreddit.
*
* @author louis
*/
public class SubReddit {
public final class SubReddit {
/**
* Subreddit's name.
@ -41,38 +48,71 @@ public class SubReddit {
/**
* Dist limit for the JSON api call.
*/
private int limit = 25;
private int limit;
/**
* Order for the JSON.
* Order for the JSON (by default, new).
*/
private String order = "new";
private String order;
/**
* Main constructor.
*
* @param name subreddit's name
*
* @throws TwitterTechSupportGoreBot.exceptions.NoSuchProperty
* @throws TwitterTechSupportGoreBot.exceptions.NoSuchFile
* @throws TwitterTechSupportGoreBot.exceptions.NotSufficientRights
* @throws TwitterTechSupportGoreBot.exceptions.NoSuchOrder
*/
public SubReddit(String name) {
public SubReddit(String name) throws NoSuchProperty, NoSuchFile,
NotSufficientRights, NoSuchOrder {
this.name = name;
ConfigFileReader reader = new ConfigFileReader();
setLimit(Integer.valueOf(reader.getProperties("reddit_posts_limit")));
setOrder(reader.getProperties("reddit_posts_sorting_order"));
this.url = "https://www.reddit.com/r/" + name + "/";
this.jsonURL = this.url.substring(0, this.url.length()) + order + ".json";
this.jsonURL
= this.url.substring(0, this.url.length()) + order + ".json";
}
/**
* Set subreddit dist limit for parsing JSON file.
*
* @param limit the limit between 1 and 100.
*/
public void setLimit(int limit) {
if (limit < 1 || limit > 100) {
throw new IllegalArgumentException("Limit should be between 1 and 100");
throw new IllegalArgumentException(
"Limit should be between 1 and 100, and it was"
+ limit + ".");
} else {
this.limit = limit;
}
}
/**
* Set order for the subreddit.
*
* @param order an order to set
*
* @throws NoSuchOrder
*/
public void setOrder(String order) throws NoSuchOrder {
String[] availableOrders
= {"new", "hot", "best", "controversial", "top", "rising"};
if (!Arrays.asList(availableOrders).contains(order)) {
throw new NoSuchOrder("This order " + order
+ "isn't allowed. Orders allowed are: "
+ Arrays.toString(availableOrders));
} else {
this.order = order;
}
}
/**
* Get subreddit dist limit.
*
* @return the limit.
*/
public int getLimit() {

View File

@ -0,0 +1,35 @@
/*
* Copyright (C) 2019 louis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package TwitterTechSupportGoreBot.socialMediaHandler;
/**
* Interface to specify what methods should social media posters implement.
*
* @author louis
*/
public interface SocialMediaPoster {
public String getSocialMediaName();
public long postText(String text);
public long postImage(String imagePath);
public long postImage(String text, String imagePath);
public long replyText(String text, long tweetId);
}

View File

@ -0,0 +1,204 @@
/*
* Copyright (C) 2019 louis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package TwitterTechSupportGoreBot.socialMediaHandler;
import TwitterTechSupportGoreBot.ConfigFileReader;
import TwitterTechSupportGoreBot.exceptions.NoSuchFile;
import TwitterTechSupportGoreBot.exceptions.*;
import java.io.File;
import twitter4j.StatusUpdate;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.conf.ConfigurationBuilder;
/**
* Twutter Bot object.
*
* @author louis
*/
public final class TwitterBot implements SocialMediaPoster {
/**
* The twitter link.
*/
private final Twitter twitter;
/**
* The Twitter API consumer key.
*/
private final String consumerKey;
/**
* The Twitter API consumer secret.
*/
private final String consumerSecret;
/**
* The Twitter API access token.
*/
private final String accessToken;
/**
* The Twitter API access secret.
*/
private final String accessSecret;
/**
* Main constructor for the Twitter Bot.
*
* @throws TwitterTechSupportGoreBot.exceptions.NoSuchFile
* @throws TwitterTechSupportGoreBot.exceptions.NotSufficientRights
* @throws TwitterTechSupportGoreBot.exceptions.NoSuchProperty
*/
public TwitterBot() throws NoSuchFile, NotSufficientRights, NoSuchProperty {
ConfigFileReader reader = new ConfigFileReader();
this.consumerKey = reader.getProperties("twitterAPI_consumerKey");
this.consumerSecret = reader.getProperties("twitterAPI_consumerSecret");
this.accessToken = reader.getProperties("twitterAPI_accessToken");
this.accessSecret = reader.getProperties("twitterAPI_accessSecret");
ConfigurationBuilder cb = new ConfigurationBuilder();
cb.setDebugEnabled(true)
.setOAuthConsumerKey(consumerKey)
.setOAuthConsumerSecret(consumerSecret)
.setOAuthAccessToken(accessToken)
.setOAuthAccessTokenSecret(accessSecret);
TwitterFactory factory = new TwitterFactory(cb.build());
this.twitter = factory.getInstance();
showScreenName();
}
/**
* Print the bot's screen name.
*/
public void showScreenName() {
try {
System.out.println("[+] Connected as user @"
+ this.twitter.getScreenName()
+ " on " + getSocialMediaName() + ".");
} catch (TwitterException te) {
System.err.println("[!] TwitterException: " + te.getMessage());
System.exit(1);
}
}
/**
* Tweet a text.
*
* @param text the text to tweet.
* @return the tweet's id.
*/
@Override
public long postText(String text) {
try {
System.out.println("[+] Tweeting \"" + text + "\".");
return twitter.updateStatus(text).getId();
} catch (TwitterException te) {
System.err.println("[!] TwitterException: " + te.getMessage());
}
return 0;
}
/**
* Tweet an image.
*
* @param imagePath the path to the image.
* @return the tweet's id.
*/
@Override
public long postImage(String imagePath) {
System.out.println("[+] Tweeting image.");
File file = new File(imagePath);
StatusUpdate status = new StatusUpdate("");
status.setMedia(file);
try {
return twitter.updateStatus(status).getId();
} catch (TwitterException te) {
System.err.println("[!] TwitterException: " + te.getMessage());
}
return 0;
}
/**
* Post an image with some text.
*
* @param text image's caption.
* @param imagePath path to the image.
* @return the tweet's id.
*/
@Override
public long postImage(String text, String imagePath) {
System.out.println("[+] Tweeting image with caption \"" + text + "\".");
File file = new File(imagePath);
StatusUpdate status = new StatusUpdate(text);
status.setMedia(file);
try {
return twitter.updateStatus(status).getId();
} catch (TwitterException te) {
System.err.println("[!] TwitterException: " + te.getMessage());
}
return 0;
}
/**
* Get the social media name.
*
* @return the social media name.
*/
@Override
public String getSocialMediaName() {
return "Twitter";
}
/**
* Get the screen name.
*
* @return the user's screen name.
*
* @throws TwitterException
*/
public String getScreenName() throws TwitterException {
return this.twitter.getScreenName();
}
/**
* Reply to a tweet.
*
* @param text the text to tweet.
* @param tweetId the tweet id.
* @return the reply's id.
*/
@Override
public long replyText(String text, long tweetId) {
System.out.println("[+] Replying \"" + text + "\" to tweet "
+ tweetId + ".");
StatusUpdate statusReply = new StatusUpdate(text);
statusReply.setInReplyToStatusId(tweetId);
try {
twitter.updateStatus(statusReply);
} catch (TwitterException ex) {
System.err.println("TwitterException: " + ex.getMessage());
}
return statusReply.getInReplyToStatusId();
}
}

View File

@ -1,82 +0,0 @@
/*
* Copyright (C) 2019 louis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package twitter_techsupportgore_bot;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
/**
* Singleton Hypervisor. This is the object that does everything. So we want it
* to be unique. That's why it's a singleton.
*
* @author louis
*/
public class Hypervisor {
/**
* Private constructor so nobody except this obect can build this object.
*/
private Hypervisor() {
}
/**
* The singleton.
*/
private static Hypervisor SINGLETON = null;
/**
* Get the Singleton Hypervisor instance.
*
* @return the instance.
*/
public static Hypervisor getSingleton() {
if (SINGLETON == null) {
SINGLETON = new Hypervisor();
}
return SINGLETON;
}
/**
* Save Image from URL. Modified version of the code that can be found atF
* https://www.programcreek.com/2012/12/download-image-from-url-in-java/
*
* @param imageUrl the image URL.
* @throws IOException
*/
public static void saveImage(String imageUrl) throws IOException {
URL url = new URL(imageUrl);
String fileName = url.getFile();
String destName = "../downloads" + fileName.substring(fileName.lastIndexOf("/"), fileName.lastIndexOf("?"));
InputStream is = url.openStream();
OutputStream os = new FileOutputStream(destName);
byte[] b = new byte[2048];
int length;
while ((length = is.read(b)) != -1) {
os.write(b, 0, length);
}
is.close();
os.close();
}
}

View File

@ -1,58 +0,0 @@
/*
* Copyright (C) 2019 louis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package twitter_techsupportgore_bot;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.util.TreeSet;
import twitter_techsupportgore_bot.reddit_handler.*;
/**
* This is where everything begins.
*
* @author louis
*/
public class Twitter_techsupportgore_bot {
/**
* Delay between two scans, in seconds.
*/
private static final int DELAY = 5;
/**
* Subreddit to extract info from.
*/
private static final String SUBREDDIT = "techsupportgore";
/**
* Launch the bot.
*
* @param args the command line arguments
* @throws java.net.MalformedURLException
* @throws java.net.ProtocolException
*/
public static void main(String[] args) throws MalformedURLException,
ProtocolException, IOException {
TreeSet<RedditPost> postsIndexed = new TreeSet<>();
RedditExtractor red = new RedditExtractor(SUBREDDIT);
for (RedditPost r : red.getRedditPosts()) {
Hypervisor.saveImage(r.getUrl());
}
}
}

View File

@ -1,181 +0,0 @@
/*
* Copyright (C) 2019 louis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package twitter_techsupportgore_bot.reddit_handler;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.util.HashSet;
/**
* Reddit extractor object.
*
* @author louis
*/
public final class RedditExtractor {
/**
* Subreddit to extract infos from.
*/
private final SubReddit sub;
/**
* Main Constructor.
*
* @param subreddit Subreddit name. Just after /r/
* @throws IOException
*/
public RedditExtractor(String subreddit) throws IOException {
if (!doesSubredditExists(subreddit)) {
throw new MalformedURLException("This subreddit (" + subreddit + ") does not exist.");
} else {
this.sub = new SubReddit(subreddit);
}
}
/**
* Check if a subreddit exists.
*
* TODO: FIND A BETTER WAY TO DO THAT
*
* @param subredditName
* @return
* @throws MalformedURLException
* @throws ProtocolException
* @throws IOException
*/
public boolean doesSubredditExists(String subredditName)
throws MalformedURLException, IOException {
String urlToTest = "https://www.reddit.com/r/" + subredditName + "/";
HttpURLConnection huc = (HttpURLConnection) (new URL(urlToTest).openConnection());
huc.setRequestProperty("User-Agent", "Mozilla 5.0 (Windows; U; "
+ "Windows NT 5.1; en-US; rv:1.8.0.11) ");
huc.setRequestMethod("HEAD");
huc.connect();
int respCode = huc.getResponseCode();
return respCode < 400;
}
/**
* Obtain the subreddit JSON response.
*
* @return the JSON from the REDDIT api.
* @throws MalformedURLException
* @throws ProtocolException
* @throws IOException
*/
public String getSubredditJson() throws MalformedURLException, ProtocolException, IOException {
HttpURLConnection con;
URL myurl = new URL(this.sub.getJsonURL());
con = (HttpURLConnection) myurl.openConnection();
try {
con.setRequestMethod("GET");
con.setRequestProperty("User-Agent", "Mozilla 5.0 (Windows; U; "
+ "Windows NT 5.1; en-US; rv:1.8.0.11) ");
StringBuilder response;
try (BufferedReader in = new BufferedReader(
new InputStreamReader(con.getInputStream()))) {
String line;
response = new StringBuilder();
while ((line = in.readLine()) != null) {
response.append(line);
response.append(System.lineSeparator());
}
return response.toString();
}
} finally {
con.disconnect();
}
}
/**
* Get Reddit's subreddit posts.
*
* @return a treeset of all the reddit posts parsed.
* @throws java.net.ProtocolException
* @throws java.net.MalformedURLException
*/
public HashSet<RedditPost> getRedditPosts() throws ProtocolException, IOException, MalformedURLException {
HashSet<RedditPost> set = new HashSet<>();
String jsonResponse = getSubredditJson();
JsonObject objet = new JsonParser().parse(jsonResponse).getAsJsonObject();
JsonObject data = new JsonParser().parse(objet.get("data").toString()).getAsJsonObject();
JsonArray children = new JsonParser().parse(data.get("children").toString()).getAsJsonArray();
for (int i = 0; i < children.size(); i++) {
JsonObject child = new JsonParser().parse(children.get(i).toString()).getAsJsonObject();
JsonObject childData = new JsonParser().parse(child.get("data").toString()).getAsJsonObject();
if (childData.get("id").toString() != null
&& !childData.get("quarantine").getAsBoolean()
&& childData.get("url").getAsString() != null) {
String id = childData.get("id").toString();
String title = childData.get("title") != null
? childData.get("title").toString() : this.sub.getName();
title = title.replace("\"", "").replace("\\", "\"");
String author = childData.get("author") != null
? childData.get("author").toString() : "anonymous";
boolean quarantine = childData.get("quarantine").getAsBoolean();
double score = childData.get("score").getAsDouble();
String postHint = childData.get("post_hint").getAsString();
boolean crosspostable = !childData.get("is_crosspostable").getAsBoolean();
boolean over18 = childData.get("over_18").getAsBoolean();
String url;
try {
JsonObject preview = new JsonParser().parse(childData.get("preview").toString()).getAsJsonObject();
JsonArray previewImages = new JsonParser().parse(preview.get("images").toString()).getAsJsonArray();
JsonObject source = new JsonParser().parse(previewImages.get(0).toString()).getAsJsonObject();
JsonObject urlSrc = new JsonParser().parse(source.get("source").toString()).getAsJsonObject();
url = urlSrc.get("url").toString().replace("amp;", "").replace("\"", "");
} catch (NullPointerException n) {
url = childData.get("url").getAsString();
}
String permalink = childData.get("url").getAsString();
boolean spoiler = childData.get("spoiler").getAsBoolean();
if (postHint.contains("video")) {
set.add(new RedditPostVideo(
id, title, quarantine, score, postHint,
crosspostable, over18, author,
permalink, spoiler, url));
} else if ("link".equals(postHint)) {
set.add(new RedditPostLink(
id, title, quarantine, score, postHint,
crosspostable, over18, author,
permalink, spoiler, url));
} else if ("text".equals(postHint)) {
set.add(new RedditPostText(
id, title, quarantine, score, postHint,
crosspostable, over18, author,
permalink, spoiler, url));
} else if ("image".equals(postHint)) {
set.add(new RedditPostImage(
id, title, quarantine, score, postHint,
crosspostable, over18, author,
permalink, spoiler, url));
}
}
}
return set;
}
}